Transact-SQL Development Experiment
Nathaniel LinSeptember 17, 20164 min read2 views

实验基础信息
开发环境:
| 操作系统
| Microsoft Windows 7
| | -------------- | --------------------------- | | 数据库管理系统 | Microsoft SQL Server 2012
| | 编程工具
| Microsoft VisualStudio 2013 | | 编程语言
| C#
|
界面演示
部分全局变量:
C# List<string> SQLToBeDone; //存储需要执行的SQL语句 Dictionary<int,string[]> values; //存储待INSERT的值 DataSet orderDetails; //数据集,存储适配器选择出来的数据 OleDbDataAdapter ordAdapter; //OleDb数据适配器
查询功能:
(见下页)

可见,程序开启时默认显示所有订单及第一个订单的详情
//显示所有订单
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]; } ```
单击某一行,显示对应行的订单详情
```C# 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]; } } ```
##### 删除功能:
选取订单明细中的一行,点击删除,选择的订单明细从表中删除
(见下页)
 
删除按钮代码如下
(见下页)
```c# 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` 中,此时还未执行)

##### 关闭功能:

续上述操作,点击关闭按钮或者右上角的X按钮时:
点击关闭按钮时,如果订单明细有修改但没有保存,则提示是否关闭,如果不关闭则返回,否则关闭程序,如果没有修改,则直接关闭(退出前检查SQLToBeDone 是否有内容,如果有则说明做出了修改)
```c#
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(); } ```
##### 保存功能:
续上述操作,选择`否` 后, 单击保存按钮
点击保存按钮时,将订单明细保存到数据库
(见下页)
```c# 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的触发器完成此功能)。

对应保存与更新功能的代码部分
通过`Dictionary` 及 `List` 数据结构,判断当前行是否已输入所有需要输入的数据
根据是否有`SalesOrderDetailID`来判断是进行插入操作还是更新操作
```C# 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. 使用哪种数据提供程序?
```c#
本程序使用了 OLEDB 类数据提供程序
- 使用的数据连接对象是哪一个?连接对象是如何建立的?最后生成的连接对象中的连接字符串是什么?代表什么含义?
连接对象为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 表示是否保存用户登录信息
- 使用的数据适配器对象是什么?其中的查询或更新语句是什么?如果有参数则参数是如何处理的?
使用的数据适配器对象为ordAdapter
查询语句为
"SELECT
* FROM Sales.SalesOrderDetail WHERE SalesOrderID = "
+ SaleOrderID.ToString();
更新语句为
"UPDATE Sales.SalesOrderDetail SET "
+ columnName
+ " = '"
+ cellValue
+ "' WHERE SalesOrderDetailID = "
+ id;
参数使用 ToString()方法 连接入SQL语句中
- 使用的数据集对象是什么?数据集中有哪些数据表?数据表是由哪些适配器对象生成的?(或采用其它方法)
使用的数据集对象为orderDetails 内部有SalesOrderDetail表,是由ordAdapter数据适配器对象生成的
还有一个数据集为AdventureDataSet 内部有SalesOrderHeader表,是由salesOrderHeaderTableAdapter数据适配器对象生成的
总结
常见问题
-
在实验过程中总是在出现数据库操作异常后程序中止,后来将数据库操作放入
try... catch语句块中后,数据库错误得以被显示出来,而且程序得以继续正常运行 -
在一开始设计器中自动生成的
cell_content_click方法下,实现点击订单列表栏目自动显示对应详情总是失败,后来更换为cell_click便成功解决了这一问题
需要改进的地方
-
程序可移植性差,部分列采用列序号硬编码方式,更换数据库后该程序可能不能再适用
-
程序界面适应性差,在窗口大小变化后,内容并不会随窗口大小变化而自适应