Transact-SQL数据库系统开发实验

SQL Server的一个练习

Posted by Nathaniel on 2016-09-17

Transact-SQL数据库系统开发实验

实验基础信息

开发环境:

操作系统 Microsoft Windows 7
数据库管理系统 Microsoft SQL Server 2012
编程工具 Microsoft VisualStudio 2013
编程语言 C#
界面演示

部分全局变量:

List<string> SQLToBeDone; //存储需要执行的SQL语句
Dictionary<int,string[]> values; //存储待INSERT的值
DataSet orderDetails; //数据集,存储适配器选择出来的数据
OleDbDataAdapter ordAdapter; //OleDb数据适配器
查询功能:

(见下页)

可见,程序开启时默认显示所有订单及第一个订单的详情

private void Form1_Load(object sender, EventArgs e)
{
//显示所有订单
this.salesOrderHeaderTableAdapter.Fill(this.adventureDataSet.SalesOrderHeader);
orderDetails = new DataSet();
//显示第一个订单详情
int SaleOrderID = Int32.Parse(this.salesGridView.Rows[0].Cells[0].Value.ToString());
String salesDetailQuery = "SELECT * FROM Sales.SalesOrderDetail WHERE SalesOrderID = " + SaleOrderID.ToString();
ordAdapter = new OleDbDataAdapter(salesDetailQuery, this.salesOrderDetailTableAdapter.Connection);
ordAdapter.Fill(orderDetails);
this.salesDetailGridView.DataSource = orderDetails.Tables[0];
}

单击某一行,显示对应行的订单详情

private void salesGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (salesGridView.SelectedRows.Count > 0)
{
int SaleOrderID = Int32.Parse(this.salesGridView.SelectedRows[0].Cells[0].Value.ToString());
String salesDetailQuery = "SELECT * FROM Sales.SalesOrderDetail WHERE SalesOrderID = " + SaleOrderID.ToString();
ordAdapter = new OleDbDataAdapter(salesDetailQuery, this.salesOrderDetailTableAdapter.Connection);
orderDetails.Clear();
ordAdapter.Fill(orderDetails);
this.salesDetailGridView.DataSource = orderDetails.Tables[0];
}
}
删除功能:

选取订单明细中的一行,点击删除,选择的订单明细从表中删除

(见下页)

删除按钮代码如下

(见下页)

private void delete_btn_Click(object sender, EventArgs e)
{
if (salesDetailGridView.SelectedRows.Count > 0)
{
int SaleOrderDetailID = Int32.Parse(this.salesDetailGridView.SelectedRows[0].Cells[1].Value.ToString());
String salesDetailDelete = "DELETE FROM Sales.SalesOrderDetail WHERE SalesOrderDetailID = " + SaleOrderDetailID.ToString();
SQLToBeDone.Add(salesDetailDelete); orderDetails.Tables[0].Rows[this.salesDetailGridView.SelectedRows[0].Index].Delete();
}
else
{
MessageBox.Show("请先选中要删除的行");
}
}

但并不真正从数据库中删除(因为生成的SQL语句存储在SQLToBeDone 中,此时还未执行)

关闭功能:

exit

续上述操作,点击关闭按钮或者右上角的X按钮时:

点击关闭按钮时,如果订单明细有修改但没有保存,则提示是否关闭,如果不关闭则返回,否则关闭程序,如果没有修改,则直接关闭(退出前检查SQLToBeDone 是否有内容,如果有则说明做出了修改)

protected override void OnFormClosing(FormClosingEventArgs e)
{
if (SQLToBeDone.Count > 0 && CloseCancel() == false)
{
e.Cancel = true;
};
}
public static bool CloseCancel()
{
const string message = "你还有未保存的修改,确认要退出吗?";
const string caption = "退出确认窗口";
var result = MessageBox.Show(message, caption,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
return true;
else
return false;
}
private void close_btn_Click(object sender, EventArgs e)
{
this.Close();
}
保存功能:

续上述操作,选择 后, 单击保存按钮

点击保存按钮时,将订单明细保存到数据库

(见下页)

private void save_btn_Click(object sender, EventArgs e)
{
bool hasError = false;
if (SQLToBeDone.Count > 0)
{
this.salesOrderDetailTableAdapter.Connection.Open();
foreach(String sql in SQLToBeDone)
{
try
{
OleDbCommand cmd = new OleDbCommand(sql, this.salesOrderDetailTableAdapter.Connection);//创建Command对象
cmd.ExecuteNonQuery();//执行命令
}
catch (Exception ex)
{
hasError = true;
MessageBox.Show(ex.Message, "数据库操作错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
this.salesOrderDetailTableAdapter.Connection.Close();
SQLToBeDone.Clear();
if (!hasError)
{
MessageBox.Show("保存成功");
}
}
}

保存按钮按下之后:

再次查询数据库:

发现对应记录已经被删除

插入功能:

可以在订单明细表的最后输入新的订单明细(在输入完一行之后,插入操作才被加入队列中)

保存后,查询数据库有:

(见下页)

更新功能:可以修改订单明细表中的某一行的内容

保存后查询数据库有:

如果输入的单价(UnitPrice)大于产品的公开报价,则提示相应的信息(利用4.6.2的触发器完成此功能)。

对应保存与更新功能的代码部分

通过DictionaryList 数据结构,判断当前行是否已输入所有需要输入的数据

根据是否有SalesOrderDetailID来判断是进行插入操作还是更新操作

private void salesGridDetailView_CellValidated(object sender, DataGridViewCellEventArgs e)
{
string cellValue = salesDetailGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
string id = salesDetailGridView.Rows[e.RowIndex].Cells[1].Value.ToString();
string columnName = salesDetailGridView.Columns[e.ColumnIndex].Name;
if (id.Length > 0)
{
//UPDATE 模式
String salesDetailUpdate = "UPDATE Sales.SalesOrderDetail SET " + columnName + " = '" + cellValue + "' WHERE SalesOrderDetailID = " + id;
SQLToBeDone.Add(salesDetailUpdate);
}
else
{
//INSERT 模式
if (!values.ContainsKey(e.RowIndex))
{
string[] lineValue = new string[11];
lineValue[e.ColumnIndex] = cellValue;
values[e.RowIndex] = lineValue;
}
else
{
values[e.RowIndex][e.ColumnIndex] = cellValue;
int length = 0;
for (int i = 0; i < 11; ++i)
{
length += (values[e.RowIndex][i] == string.Empty || values[e.RowIndex][i] == null ? 0 : 1);
}
string salesDetailInsert = "INSERT INTO Sales.SalesOrderDetail VALUES(";
if (length == 9)
{
for (int i = 0; i < 11; ++i)
{
if (i != 1 && i != 8)
{
salesDetailInsert += "'";
salesDetailInsert += values[e.RowIndex][i];
salesDetailInsert += "'";
if (i != 10)
{
salesDetailInsert += ",";
}
}
}
salesDetailInsert += ")";
SQLToBeDone.Add(salesDetailInsert);
values.Remove(e.RowIndex);
}
}
}
}

问题解答

  1. 使用哪种数据提供程序?

    本程序使用了 OLEDB 类数据提供程序
  2. 使用的数据连接对象是哪一个?连接对象是如何建立的?最后生成的连接对象中的连接字符串是什么?代表什么含义?

    连接对象为this.salesOrderDetailTableAdapter.Connection
    是使用salesOrderDetailTableAdapter 使用SQL语句 连接对应的数据库 建立的
    连接字符串如下:
    Provider=SQLNCLI11;Data Source=Nathaniel-PC;Persist Security Info=True;Password=123123;User ID=test_login;Initial Catalog=AdventureWorks2012
    含义如下
    Provider 为客户端组件的驱动 Data Source 为数据源的名称
    Password 为连接数据库的密码 User ID 为连接数据库的登录名
    Initial Catalog为默认使用的数据库 Persist Security Info 表示是否保存用户登录信息
  3. 使用的数据适配器对象是什么?其中的查询或更新语句是什么?如果有参数则参数是如何处理的?

    使用的数据适配器对象为ordAdapter
    查询语句为
    "SELECT * FROM Sales.SalesOrderDetail WHERE SalesOrderID = " + SaleOrderID.ToString();
    更新语句为
    "UPDATE Sales.SalesOrderDetail SET " + columnName + " = '" + cellValue + "' WHERE SalesOrderDetailID = " + id;
    参数使用 ToString()方法 连接入SQL语句中
  4. 使用的数据集对象是什么?数据集中有哪些数据表?数据表是由哪些适配器对象生成的?(或采用其它方法)

    使用的数据集对象为orderDetails 内部有SalesOrderDetail表,是由ordAdapter数据适配器对象生成的
    还有一个数据集为AdventureDataSet 内部有SalesOrderHeader表,是由salesOrderHeaderTableAdapter数据适配器对象生成的

总结

常见问题

  1. 在实验过程中总是在出现数据库操作异常后程序中止,后来将数据库操作放入try... catch语句块中后,数据库错误得以被显示出来,而且程序得以继续正常运行
  2. 在一开始设计器中自动生成的cell_content_click 方法下,实现点击订单列表栏目自动显示对应详情总是失败,后来更换为cell_click便成功解决了这一问题

需要改进的地方

  1. 程序可移植性差,部分列采用列序号硬编码方式,更换数据库后该程序可能不能再适用
  2. 程序界面适应性差,在窗口大小变化后,内容并不会随窗口大小变化而自适应