Nathaniel's blog
Back to posts

Transact-SQL Development Experiment

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

实验基础信息

开发环境:

| 操作系统

| 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数据适配器

查询功能:

(见下页)

select

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


 	//显示所有订单

 	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]; 	} } ```

##### 删除功能:

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

(见下页)

![del1](https://minio.nate-lin.com/vlepo/user-images/4916dbb3-6474-42b7-a14c-b2d7222369b6.png) ![del2](https://minio.nate-lin.com/vlepo/user-images/a9a7812a-ef22-4af0-a0de-d2c8af5d9c3f.png)

删除按钮代码如下

(见下页)

```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` 中,此时还未执行)

![del3](https://minio.nate-lin.com/vlepo/user-images/bb328edd-35ee-469f-8a2f-dea3e9c2076a.png)

##### 关闭功能:

![exit](https://minio.nate-lin.com/vlepo/user-images/9e077e2a-c7e4-47b6-a476-426e5370183c.png)

续上述操作,点击关闭按钮或者右上角的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("保存成功");

 }

} } ```

保存按钮按下之后: 	![save1](https://minio.nate-lin.com/vlepo/user-images/9cdf296b-cb30-4c6d-8d82-7a4f201e6470.png)

再次查询数据库:![save2](https://minio.nate-lin.com/vlepo/user-images/80ca175b-b38c-455c-971c-04b0aa0f042b.png)

发现对应记录已经被删除

##### 插入功能:

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

![insert1](https://minio.nate-lin.com/vlepo/user-images/04a92344-9881-4514-9abc-9f9eaa11c4a0.png)

保存后,查询数据库有:

(见下页)

![insert2](https://minio.nate-lin.com/vlepo/user-images/a53a16a3-137a-4132-9fb8-b857e3f25a75.png)

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

![update1](https://minio.nate-lin.com/vlepo/user-images/30198a06-f7f8-4d32-bdee-e8ed4cf0088e.png)

保存后查询数据库有:

![update2](https://minio.nate-lin.com/vlepo/user-images/6f60839c-bfec-405f-830b-ac6dd6bdc381.png)

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

![update3](https://minio.nate-lin.com/vlepo/user-images/2c708de0-8183-4e5e-b7cc-451743b1deff.png)

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

通过`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 类数据提供程序

  1. 使用的数据连接对象是哪一个?连接对象是如何建立的?最后生成的连接对象中的连接字符串是什么?代表什么含义?

连接对象为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 表示是否保存用户登录信息

  1. 使用的数据适配器对象是什么?其中的查询或更新语句是什么?如果有参数则参数是如何处理的?

使用的数据适配器对象为ordAdapter

查询语句为

"SELECT

* FROM Sales.SalesOrderDetail WHERE SalesOrderID = "

+ SaleOrderID.ToString();

更新语句为

"UPDATE Sales.SalesOrderDetail SET "

+ columnName

+ " = '"

+ cellValue

+ "' WHERE SalesOrderDetailID = "

+ id;

参数使用 ToString()方法 连接入SQL语句中
  1. 使用的数据集对象是什么?数据集中有哪些数据表?数据表是由哪些适配器对象生成的?(或采用其它方法)

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

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

总结

常见问题

  1. 在实验过程中总是在出现数据库操作异常后程序中止,后来将数据库操作放入try... catch语句块中后,数据库错误得以被显示出来,而且程序得以继续正常运行

  2. 在一开始设计器中自动生成的cell_content_click 方法下,实现点击订单列表栏目自动显示对应详情总是失败,后来更换为cell_click便成功解决了这一问题

需要改进的地方

  1. 程序可移植性差,部分列采用列序号硬编码方式,更换数据库后该程序可能不能再适用

  2. 程序界面适应性差,在窗口大小变化后,内容并不会随窗口大小变化而自适应

Share this post

Reactions