【C#Windows 窗体应用】实现简单学生查询成绩,管理员管理学生成绩功能

已经上传到 https://gitee.com/biflcy/student-management-system
给颗小心心 >.<

一、设计窗体

1.登录界面窗体(Form1.cs)

(1)构建登录界面添加控件

(2)编写验证码功能

  //定义一个函数private void GetVerifyCode(){//创建位图Bitmap Bmp = new Bitmap(121, 39);//根据新建的Bitmap位图,创建画布Graphics Grc = Graphics.FromImage(Bmp);//创建随机数对象(不重复)Random Rdm = new Random(Guid.NewGuid().GetHashCode());//定义一个字符串存放产生的随即字符串string sRnd = "";for (int i = 0; i < 5; i++){int iNum = Rdm.Next(0, 10);sRnd += iNum.ToString();}//定义数组存放字体string[] Fonts = { "幼圆","微软雅黑","宋体","楷体","隶书"};Color[] Colors = {Color.Black,Color.Yellow,Color.Green,Color.HotPink,Color.Blue};//定义数组存放字体颜色for (int i = 0; i < 5; i++){   //创建坐标Point point = new Point(i*25, 0);int num1 = Rdm.Next(0, 5);int num2 = Rdm.Next(0, 5);Grc.DrawString(sRnd[i].ToString(), new Font(Fonts[num1], 16), new SolidBrush(Colors[num2]), point);}PbImgCode.Image = Bmp;//实现噪点,防止暴力破解int iBmpx = Bmp.Width;int iBmpy = Bmp.Height;for (int i = 0; i < 25; i++){//位图中划线Point pStart = new Point(Rdm.Next(0,iBmpx),Rdm.Next(0,iBmpy));Point pEnd = new Point(Rdm.Next(0,iBmpx), Rdm.Next(0,iBmpy));Grc.DrawLine(new Pen(Color.Gray),pStart,pEnd);}for (int i = 0; i < 1000; i++){//添加噪点Point pRnd = new Point(Rdm.Next(0, iBmpx), Rdm.Next(0, iBmpy));Bmp.SetPixel(pRnd.X,pRnd.Y,Color.Gray);}}

在窗体加载时候显示

 private void Form1_Load(object sender, EventArgs e){GetVerifyCode();}

在点击验证码时切换

private void pictureBox2_Click(object sender, EventArgs e){GetVerifyCode();}

将光标变为小手增加指示效果

最后成果

2.学生课程成绩信息窗体(Form3.cs)

(1)学生可查看自己的课程成绩和重修情况


在这里插入图片描述

(2)底部工具栏设置实时时间显示

在窗体加载时显示时间

 public Form2(){InitializeComponent();toolStripStatusLabel3.Text = DateTime.Now.ToString("G");timer1.Start();}

3.添加时钟响应

private void timer1_Tick(object sender, EventArgs e){toolStripStatusLabel3.Text = DateTime.Now.ToString("G");}

(3)学生可以修改自己的账号密码(Form41 .cs)

3.管理员窗体

(1)管理员初始界面(Form2.cs)

(2)管理员可用来编写学生信息输入窗体(Form21.cs)

(3)管理员管理学生账号窗口(Form4.cs)

二、设计数据库(SQL)

1.学生成绩管理数据表(dbo.score)


用查询语句添加数据信息

insert into score values(
'1','小明','计算机',90,80
)
insert into score values(
'2','小红','通信',40,50
)
insert into score values(
'3','小绿','机电',70,70
)
insert into score values(
'4','小黄','设计',60,30
)

2.学生账户管理数据表(dbo.account)


用查询语句添加数据信息

insert into account values(
'1','小明','123456','一块饼干'
)
insert into account values(
'2','小红','123456','一口奶茶'
)
insert into account values(
'3','小绿','123456','一勺布丁'
)
insert into account values(
'4','小黄','123456','一碗泡面'
)

3.学生成绩查询数据表(dbo.query)

用查询语句添加数据信息

insert into query values(
'CS01','计算机','4'
)
insert into query values(
'CS02','数据库','3'
)

4.查询结果显示

查询语句

select* from account
select* from query
select* from score

三、数据库和项目相连

1.在项目中添加组件类

(1)添加两个命名空间

using System.Data;
using System.Data.SqlClient;

(2)定义方法连接数据库并操作

    using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace MySummertime
{class Dao{public SqlConnection connection(){//连接字符串可在连接到数据库的高级选项中直接复制string str = "Data Source=LAPTOP-TLA9UJQE;Initial Catalog=MyTime;Integrated Security=True";SqlConnection sc = new SqlConnection(str);//打开连接sc.Open();return sc;}public SqlCommand command(string sql){connection();SqlCommand sc = new SqlCommand(sql, connection());return sc;}//用于增删改public int Excute(string sql){//返回受影响的行数return command(sql).ExecuteNonQuery();}//用于查找public SqlDataReader read(string sql){//返回查询对象return command(sql).ExecuteReader();}}
}

2.通过dbo.account表查询实现学生登录

(1)登录时出现错误发出提示,通过用户密码查询登录

private bool login(){bool UserPassword = false;bool administrators = false;bool VerificationCode = false;//验证用户名,密码是否正确if (textBox1.Text == ""){MessageBox.Show("请输入正确的用户名或者学号","提示",MessageBoxButtons.OK,MessageBoxIcon.Warning);return false;}if (textBox2.Text == ""){MessageBox.Show("密码不可以为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);return false;}if (comboBox1.Text == ""){MessageBox.Show("请选择权限", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);return false;}if (textBox3.Text == ""){MessageBox.Show("验证码不可以为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);return false;}if (comboBox1.Text == "学生"){//学生可以根据学号进行登入string sql_1 = "select *from account where username ='" + textBox1.Text + "'and password = '" + textBox2.Text+"'";//这是之前连接数据库的函数,定义对象Dao dao = new Dao();//返回查询结果IDataReader dr1 = dao.read(sql_1);//返回的是bool值 看有没有这条记录if (dr1.Read()){UserPassword = true;}else{MessageBox.Show("用户名和密码不匹配", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);textBox1.Text = null;textBox2.Text = null;textBox3.Text = null;GetVerifyCode();return false;}}if (comboBox1.Text == "管理员"){if (textBox1.Text == "lcymdmin" && textBox2.Text == "123456"){administrators = true;}else{MessageBox.Show("用户名和密码不匹配", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);textBox1.Text = null;textBox2.Text = null;textBox3.Text = null;GetVerifyCode();return false;}}//这里定义了一个全局变量用来接收产生的随即验证码if (textBox3.Text == codeText){VerificationCode = true;}else{MessageBox.Show("验证码输入错误请重新输入", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);//重新加载验证码GetVerifyCode();textBox3.Text = null;}if (VerificationCode && (UserPassword||administrators)){return true;}return false;}

(2)登录成功时,标题图片向下移动

这里添加了计时器来控制图片移动

 private void button1_Click(object sender, EventArgs e){if (login()){timer1.Start();button1.Visible = false;button2.Visible = false;label1.Visible = false;label2.Visible = false;label3.Visible = false;label4.Visible = false;textBox1.Visible = false;textBox2.Visible = false;textBox3.Visible = false;PbImgCode.Visible = false;}}
private void timer1_Tick(object sender, EventArgs e){if (pictureBox1.Location.Y < 160){pictureBox1.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y + 1);}else{timer1.Stop();}}

(3)登录成功后的效果

(4)实现学生、管理员登录自己特定的窗口

private void timer1_Tick(object sender, EventArgs e){if (pictureBox1.Location.Y < 160){pictureBox1.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y + 1);}else{if (comboBox1.Text == "学生"){Form3 form3 = new Form3();form3.Show();this.Hide();}else{Form2 form2 = new Form2();form2.Show();this.Hide();}timer1.Stop();}}}

(5)实现用完关闭程序功能

private void Form3_FormClosed(object sender, FormClosedEventArgs e){//结束整个程序Application.Exit();}

3.数据库将数据传输到窗体显示表中

(1)在Form调用构造函数

public Form2(){InitializeComponent();toolStripStatusLabel3.Text = DateTime.Now.ToString("G");timer1.Start();//调用构造方法Table();}

(2)在表控件中传入数据库的记录

 private void Table(){//这里用一个刷新记录的语句,方便接下来增删改操作的执行dataGridView1.Rows.Clear();string sql = "select *from Student";Dao dao = new Dao();IDataReader dr = dao.read(sql);while(dr.Read()){//如果当前有记录,将记录转化成字符存入定义的字符串中string sid, sname, scs, score1, score2;sid = dr["id"].ToString();sname = dr["name"].ToString();scs = dr["system"].ToString();score1 = dr["Comscore"].ToString();score2 = dr["Datascore"].ToString();//将数据传入控件中string[] str = { sid, sname, scs, score1, score2 };dataGridView1.Rows.Add(str);}//关闭读取连接dr.Close();}

(3)导入成功效果

四、管理员功能实现

1.完成学生成绩表增加功能

(1)当点击到查询按钮的时候,我们使当前窗体跳转到信息输入的对话框

     private void 增加学生信息ToolStripMenuItem_Click(object sender, EventArgs e){Form21 form21 = new Form21();form21.ShowDialog();}

(2)在From21.cs中当我鼠标点击保存时增加一行记录

     private void button1_Click(object sender, EventArgs e){if(textBox1.Text==""|| textBox2.Text == "" || textBox3.Text == "" || textBox4.Text == "" || textBox5.Text == ""){MessageBox.Show("填写项不可以为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);}else{string sql = "insert into score values('" + textBox1.Text + "','" + textBox2.Text + "','" + textBox3.Text + "','" + textBox4.Text + "','" + textBox5.Text + "')";Dao dao = new Dao();int i=dao.Excute(sql);if (i > 0){MessageBox.Show("插入成功");textBox1.Text = null;textBox2.Text = null;textBox3.Text = null;textBox4.Text = null;textBox5.Text = null;}}}

(3)当鼠标点击取消时,清空文本编辑框中的内容

private void button2_Click(object sender, EventArgs e){textBox1.Text = null;textBox2.Text = null;textBox3.Text = null;textBox4.Text = null;textBox5.Text = null;}

(4)当点击刷新按钮时,进行数据的重置导入

这就是为什么之前我们要在table方法体中增加clear()了

private void toolStripButton3_Click(object sender, EventArgs e){Table();}

2.完成学生成绩表删除功能

(1)当我们选定一行记录的时候点击删除选项的时候,就执行了删除语句

private void 删除学生信息ToolStripMenuItem_Click(object sender, EventArgs e){DialogResult r = MessageBox.Show("确定要删除?", "提示", MessageBoxButtons.OKCancel);if (r == DialogResult.OK){string id, name;id = dataGridView1.SelectedCells[0].Value.ToString();name = dataGridView1.SelectedCells[1].Value.ToString();string sql = "delete from score where id='" + id + "'and name='" + name + "'";Dao dao = new Dao();dao.Excute(sql);Table();}}

(2)注意设置选择的类型

因为我们是从一整行记录中进行学号姓名的进行删除匹配的,所以要设置用户选择整行记录

3.完成学生成绩表修改功能

(1)当点击修改按钮时把当前捕获的数据传输到From21.cs中

private void 修改学生信息ToolStripMenuItem_Click(object sender, EventArgs e){//写入一个数组,每一行元素就是我们选中的数据string[] str = { dataGridView1.SelectedCells[0].Value.ToString(),dataGridView1.SelectedCells[1].Value.ToString(),dataGridView1.SelectedCells[2].Value.ToString(),dataGridView1.SelectedCells[3].Value.ToString(),dataGridView1.SelectedCells[4].Value.ToString() };Form21 form21 = new Form21(str);form21.ShowDialog();}

(2)传输数据我们需要重载函数

//需要一个数组记录初始值string[] str = new string[5];public Form21(string[] a){InitializeComponent();for (int i = 0; i < 5; i++){str[i] = a[i];}//重新将数据加载到编辑框中textBox1.Text = str[0];textBox2.Text = str[1];textBox3.Text = str[2];textBox4.Text = str[3];textBox5.Text = str[4];}

(3)点击修改按钮时执行修改操作

这里是点击按钮后对比初始值,如果与初始值不同就进行修改操作

private void button3_Click(object sender, EventArgs e){if (textBox1.Text == "" || textBox2.Text == "" || textBox3.Text == "" || textBox4.Text == "" || textBox5.Text == ""){MessageBox.Show("修改项不可以为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);}else{string sql;Dao dao = new Dao();if (textBox1.Text != str[0]){sql = "update score set id='" + textBox1.Text + "'where id='" + str[0] + "'and name='" + str[1] + "'";dao.Excute(sql);str[0] = textBox1.Text;}if (textBox2.Text != str[1]){sql = "update score set name='" + textBox2.Text + "'where id='" + str[0] + "'and name='" + str[1] + "'";dao.Excute(sql);str[1] = textBox2.Text;}if (textBox3.Text != str[2]){sql = "update score set system='" + textBox3.Text + "'where id='" + str[0] + "'and name='" + str[1] + "'";dao.Excute(sql);str[2] = textBox3.Text;}if (textBox4.Text != str[3]){sql = "update score set Comscore='" + textBox4.Text + "'where id='" + str[0] + "'and name='" + str[1] + "'";dao.Excute(sql);str[3] = textBox4.Text;}if (textBox5.Text != str[1]){sql = "update score set Datascore='" + textBox5.Text + "'where id='" + str[0] + "'and name='" + str[1] + "'";dao.Excute(sql);str[4] = textBox5.Text;}MessageBox.Show("修改完成");}

(4)选择显示保存和修改按钮

我们要在原先的成绩编辑界面,增加一个按钮,显示文本为修改
接着在不同功能的窗体下隐藏我们不需要的按钮

public Form21(){InitializeComponent();button3.Visible = false;//当插入数据时保存按钮不可见}//需要一个数组记录初始值string[] str = new string[5];public Form21(string[] a){InitializeComponent();for (int i = 0; i < 5; i++){str[i] = a[i];}//重新将数据加载到编辑框中textBox1.Text = str[0];textBox2.Text = str[1];textBox3.Text = str[2];textBox4.Text = str[3];textBox5.Text = str[4];button1.Visible = false;//当修改时保存按钮不可见}

4.功能优化

(1)自动刷新功能

在之前的增加,修改操作时都需要人为刷新才可以显示最新的结果,现在优化使操作可以自动刷新
可以在修改窗口关闭后再调用Table(),就实现了系统刷新

private void 增加学生信息ToolStripMenuItem_Click(object sender, EventArgs e){Form21 form21 = new Form21();form21.ShowDialog();Table();}private void 修改学生信息ToolStripMenuItem_Click(object sender, EventArgs e){//写入一个数组,每一行元素就是我们选中的数据string[] str = { dataGridView1.SelectedCells[0].Value.ToString(),dataGridView1.SelectedCells[1].Value.ToString(),dataGridView1.SelectedCells[2].Value.ToString(),dataGridView1.SelectedCells[3].Value.ToString(),dataGridView1.SelectedCells[4].Value.ToString() };Form21 form21 = new Form21(str);form21.ShowDialog();Table();}

5.完成账户管理表查询功能

 public Form4(){InitializeComponent();Table();}private void Table(){string sql = "select *from account";Dao dao = new Dao();IDataReader dr = dao.read(sql);while (dr.Read()){//如果当前有记录,将记录转化成字符存入定义的字符串中string sid, sname, password, username;sid = dr["id"].ToString();sname = dr["name"].ToString();password = dr["password"].ToString();username = dr["username"].ToString();//将数据传入控件中string[] str = { sid, sname, password, username };dataGridView1.Rows.Add(str);}//关闭读取连接dr.Close();}

五、学生查询系统的实现

1.实现成绩查询

(1)在From1中,如果是学生权限登录,将学号和用户名传到查询窗口From3中

 private void timer1_Tick(object sender, EventArgs e){if (pictureBox1.Location.Y < 160){pictureBox1.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y + 1);}else{if (comboBox1.Text == "学生"){ string sql = "select *from account where username ='" + textBox1.Text + "'and password = '" + textBox2.Text+"'";Dao dao = new Dao();IDataReader dr = dao.read(sql);dr.Read();string sID = dr["id"].ToString();Form3 form3 = new Form3(sID, textBox1.Text);form3.Show();this.Hide();}else{Form2 form2 = new Form2();form2.Show();this.Hide();}timer1.Stop();}}}

(2)根据用户名和学号,传入数据,更新状态栏

传入数据

private void Table(){string sql = "select *from query";string sql_1 = "select *from score where id='" + sid + "'";Dao dao = new Dao();IDataReader dr = dao.read(sql);while (dr.Read()){//如果当前有记录,将记录转化成字符存入定义的字符串中string cid, cname,credit,grade,rebuit;grade = "0";cid = dr["cid"].ToString();cname = dr["cname"].ToString();credit = dr["credit"].ToString();IDataReader dr_1 = dao.read(sql_1);if (cid == "CS01"){dr_1.Read();grade = dr_1["Comscore"].ToString();}if(cid == "CS02"){dr_1.Read();grade = dr_1["Datascore"].ToString();}if(Convert.ToInt32(grade)>=60){rebuit = "不用重修";}else{rebuit = "需要重修";}//将数据传入控件中string[] str = { cid, cname, credit, grade, rebuit };dataGridView1.Rows.Add(str);dr_1.Close();}//关闭读取连接dr.Close();}

更新状态栏

 public Form3(string s,string user){sid = s;InitializeComponent();toolStripStatusLabel3.Text = DateTime.Now.ToString("G");toolStripStatusLabel1.Text = "欢迎用户:" + user + "登录";timer1.Start();Table();}

(3)最终效果

2.实现更改用户名和密码功能

(1)当点击更改用户及密码时加载Form41用户更改窗口

private void 更改用户名和密码ToolStripMenuItem_Click(object sender, EventArgs e){Form41 from41 = new Form41();from41.ShowDialog();}

(2)在修改窗体完善修改功能

 private void button1_Click(object sender, EventArgs e){if ( textBox3.Text == "" || textBox4.Text == "" ){MessageBox.Show("填写项不可以为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);}else{string sql;Dao dao = new Dao();sql = "update account set username='" + textBox3.Text + "'where id='" + Csid + "'";dao.Excute(sql);sql = "update account set password='" + textBox4.Text + "'where id='" + Csid + "'";dao.Excute(sql);MessageBox.Show("修改完成");}}

关于项目中的错误及解决方法

1. C#.Net Core控制台程序连接SQL Server SqlConnection类报错

(1)在程序包控制台输入命令

PM>  Install-Package System.Data.Common
PM>  Install-Package System.Data.SqlClient

(2)或者在包管理器添加“System.Data.Common”和“System.Data.SqlClient”两个引用

问题成功解决

【C#Windows 窗体应用】实现简单学生查询成绩,管理员管理学生成绩相关推荐

  1. C# Windows 窗体的.Net 框架绘图技术

    当编写一个典型的Windows 窗体程序时,窗体和控件的绘制.效果等操作是不需要特别加以考虑的.这是为什么呢?因为通过使用 .Net 框架,开发人员可以拖动一系列的控件到窗体上,并书写一些简单的与事件 ...

  2. 简单航班查询系统java_基于jsp的航班信息查询-JavaEE实现航班信息查询 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的航班信息查询, 该项目可用各类java课程设计大作业中, 航班信息查询的系统架构分为前后台两部分, 最终实现在线上 ...

  3. 学生上课考勤系统jsp_基于jsp的学生课堂考勤-JavaEE实现学生课堂考勤 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的学生课堂考勤, 该项目可用各类java课程设计大作业中, 学生课堂考勤的系统架构分为前后台两部分, 最终实现在线上 ...

  4. java实现学生财务管理_基于jsp的学生社团财务管理-JavaEE实现学生社团财务管理 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的学生社团财务管理, 该项目可用各类java课程设计大作业中, 学生社团财务管理的系统架构分为前后台两部分, 最终实 ...

  5. HTML学生考勤界面代码,基于jsp的学生考勤系统-JavaEE实现学生考勤系统 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的学生考勤系统, 该项目可用各类java课程设计大作业中, 学生考勤系统的系统架构分为前后台两部分, 最终实现在线上 ...

  6. 请假代码java web_基于jsp的学生请假管理系统-JavaEE实现学生请假管理系统 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的学生请假管理系统, 该项目可用各类java课程设计大作业中, 学生请假管理系统的系统架构分为前后台两部分, 最终实 ...

  7. java学生请假天数代码_基于jsp的学生网上请假-JavaEE实现学生网上请假 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的学生网上请假, 该项目可用各类java课程设计大作业中, 学生网上请假的系统架构分为前后台两部分, 最终实现在线上 ...

  8. .net学习笔记——学生信息管理系统(二、windows窗体实现登录界面)

    (学习目标:使用.net 窗体制作一个学生信息管理系统,满足学生信息.班级信息.年级信息的增删改查.) 第二天:登录界面的实现 任务:在Microsoft Visual Studio中新建一个Wind ...

  9. C#创建Windows窗体应用程序实例6【ASCII码查询程序】

     都实例6了?你还不会创建项目?不会操作步骤?不会写代码?请进入下方链接学习吧! C#创建Windows窗体应用程序实例1https://blog.csdn.net/qq_45037155/artic ...

最新文章

  1. Python pandas模块输出每行中间省略号问题
  2. 如何给UI上可以接收focus事件的element动态注册onfocus处理函数
  3. CMOS图像传感器——噪声模型
  4. java文件替换一行数据_用Golang替换文件中的一行
  5. 数据结构和算法分析英语生词整理
  6. 电子相册系统(九)分页
  7. 2022数学建模“五一杯”B题 题解+论文
  8. 淘宝店铺的装修是店铺的门面,如何进行淘宝店铺装修?需要注意的点有哪些?
  9. 数据可视化Error:matplotlib is required for plotting when the default backend “matplotlib“ is selected
  10. 支付宝小程序沙箱支付提示(系统繁忙,请稍后再试)
  11. ol-地图上添加图标
  12. Windows Installer:正在安装其他程序。请等待该安装完成,然后再次尝试安装此软件
  13. 将文件从 Linux 传输到 Windows
  14. 详细介绍用MATLAB实现基于A*算法的路径规划(附完整的代码,代码逐行进行解释)(一)--------A*算法简介和环境的创建
  15. IDEA 2020.1汉化问题解决办法
  16. 机器学习之Boltzmann 机算法
  17. WIN7 开启PAE突破4G内存使用限制
  18. 产品运营必看!这6本书你不该错过!
  19. Matplotlib Python 画图工具包教程学习笔记4 等高线图以及3D图形的画法
  20. shell bash shell 语法中的字符串拼接 合并

热门文章

  1. SKY65723-81低噪声放大器前端模块 GPS / GNSS / BDS预过滤器
  2. iit delhi_IIT的完整形式是什么?
  3. java+subject+login_Java Subject類代碼示例
  4. 深圳绿道最全资料合集_我是亲民_新浪博客
  5. 手把手教你做树莓派魔镜-MagicMirror(二)-烧写系统卡
  6. 用python画竹子_初识Python
  7. SQL CHECK 约束
  8. 汽车保养技巧 十大汽车保养技巧
  9. calipso是什么意思_fub是什么意思_fub怎么读_fub翻译_用法_发音_词组_同反义词_小而胖的人-新东方在线英语词典...
  10. oracle数据库中_以下undo和redo说法错误的是,[案例]Oracle报错ORA-01157 ORA-01110 12C数据库undo异常恢复...