分类:.Net编程类 |
1.1 创建SqlCommand对象
可以通过三种方式创建SqlCommand对象。第一种方式就是使用new关键字直接创建对象的一个新实例,然后设置适当属性;还可以使用一个可用的构造函数来指定查询字符串以及 SqlConnection对象;第三种方式是调用SqlConnection类(第3章对这个类进行了介绍)的CreaterCommand方法。这三种方法如下所示:
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
strSQL = "SELECT CustomerID, CompanyName FROM Customers";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
SqlCommand cmd;
//使用无参数构造函数
cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandText = strSQL;
//使用参数化构造函数
cmd = new SqlCommand(strSQL, cn);
//使用Connection对象的CreateCommand方法
cmd = cn.CreateCommand();
cmd.CommandText = strSQL;
1.2 使用SqlCommand执行查询
了解了如何创建SqlCommand对象,下面介绍使用SqlCommand执行查询的基本情况。
1.3 执行返回行的查询
SqlCommand的最常见用法是执行返回结果的查询。例如要获取一特定国家(可能是加拿大)发货订单的信息,可以使用如下所示的查询:
SELECT OrderID, CustomerID, OrderDate, ShippedDate, ShipCity
FROM Orders WHERE ShipCountry = 'Canada'
要执行此查询,首先需要将SqlCommand对象的CommandText属性设置为包含该查询文本的一个字符串。然后调用SqlCommand对象的ExecuteReader方法,如以下代码所示:
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
strSQL = "SELECT OrderID, CustomerID, OrderDate, ShippedDate, ShipCity" +
" FROM Orders WHERE ShipCountry = 'Canada'";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
SqlCommand cmd = cn.CreateCommand();
cmd.CommandText = strSQL;
SqlDataReader rdr = cmd.ExecuteReader();
1.4 获取单一值
在某些时候,可能希望执行一个返回单一值的查询。例如执行一个简单查询,以确定在一个表中存在多少行,或者更复杂一些,如确定一个特定客户所下订单的总数是多少,可以使用类似于以下代码的查询:
SELECT SUM([Order Details].UnitPrice * [Order Details].Quantity)
FROM Orders INNER JOIN [Order Details]
ON Orders.OrderID = [Order Details].OrderID
WHERE Orders.CustomerID = 'ALFKI'
诸如此类的查询在许多应用程序中都很常见。以下代码段执行此查询,并将订单总值输出到控制台窗口。其中,创建一个SqlDataReader、调用Read、检查第一行第一列的内容,然后再关闭
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
strSQL = "SELECT SUM(UnitPrice * Quantity) " +
" FROM Orders INNER JOIN [Order Details]" +
" ON Orders.OrderID = [Order Details].OrderID" +
" WHERE CustomerID = 'ALFKI'";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
SqlCommand cmd = new SqlCommand(strSQL, cn);
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
decimal decOrderTotal = (decimal)rdr[0];
rdr.Close();
Console.WriteLine("Order Total: {0:c}", decOrderTotal);
为了帮助简化此类情况,SqlCommand类公开了一个ExecuteScalar方法。ExecuteScalar方法不是返回一个SqlDataReader,而是返回在一般Object数据类型中第一行第一列的值。此类方法通常称为“syntactic sugar”(语法甜头),至少在我工作的地方是这样称呼它的。换言之,调用ExecuteScalar有效地创建了一个SqlDataReader,并获取期望信息,但不需要编写全部代码。然后,ExecuteScalar关闭并处置SqlDataReader。
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
strSQL = "SELECT SUM(UnitPrice * Quantity) " +
" FROM Orders INNER JOIN [Order Details]" +
" ON Orders.OrderID = [Order Details].OrderID" +
" WHERE CustomerID = 'ALFKI'";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
SqlCommand cmd = new SqlCommand(strSQL, cn);
decimal decOrderTotal = (decimal)cmd.ExecuteScalar();
Console.WriteLine("Order Total: {0:c}", decOrderTotal);
1.5 执行不返回结果集的查询
不返回结果集的查询通常被称为“操作查询(action query)”,在本书中有时会使用这一术语。操作查询共有两大类。
l 数据操作语言(DML)查询 也称为“基于查询的更新(QBU)”,这些查询修改数据库的内容。下面是一些例子:
INSERT INTO Customers (CustomerID, CompanyName)
VALUES ('NewID', 'NewCustomer')
UPDATE Customers SET CompanyName = 'NewCompanyName'
WHERE CustomerID = 'ALFKI'
DELETE FROM Customers WHERE CustomerID = 'ALFKI'
l 数据定义语言(DDL)查询 这些查询修改数据库的结构,如下例所示:
CREATE TABLE Table1 (Field1 int NOT NULL
CONSTRAINT PK_Table1 PRIMARY KEY,
Field2 varchar(32))
ALTER VIEW View1 AS SELECT Field1, Field2 FROM Table1
DROP PROCEDURE StoredProcedure1
可以通过调用SqlCommand的 ExecuteReader方法来执行这些查询。但是,因为这些查询不返回任何行,所以似乎是一些不必要的开销。幸运的是,还有更简单的方法。 SqlCommand类公开了ExecuteNonQuery方法,该方法执行查询而不返回SqlDataReader对象。下面的例子说明如何使用这一特性。
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
strSQL = "UPDATE Customers SET CompanyName = 'NewValue' " +
"WHERE CustomerID = 'ALFKI'";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
SqlCommand cmd = new SqlCommand(strSQL, cn);
//cmd.ExecuteNonQuery();
int intRecordsAffected = cmd.ExecuteNonQuery();
if (intRecordsAffected == 1)
Console.WriteLine("Update succeeded");
else
//Assume intRecordsAffected = 0
Console.WriteLine("Update failed");
1.6 执行批量操作查询
假定您希望成批执行一系列操作查询。例如,假定有一个Products表,并希望根据产品目录改变一些产品的单价,一些标为涨价,一些标为降价。可以将以下查询放在一起,然后同时执行它们。
UPDATE Products SET UnitPrice = UnitPrice * 0.85 WHERE CategoryID = 3;
UPDATE Products SET UnitPrice = UnitPrice * 1.15 WHERE CategoryID = 4;
UPDATE Products SET UnitPrice = UnitPrice * 0.75 WHERE CategoryID = 5;
如果希望知道每个查询影响了多少行,应当怎么办呢?理想情况下,可能会调用一个简单的方法,例如ExecuteNonQuery,使此方法返回一个值的数组,其中,数组的项对应于由批处理中各个查询所修改的行数。
ExecuteReader和ExecuteNonQuery方法都不能独自完成此功能。这两种方法都允许执行批量查询,但都不能告知每个查询所修改的行数。ExecuteNonQuery方法的返回值将告知整个批处理修改了多少行,但这一信息也许不能满足您的需要。
ADO.NET 2.0中的(新的和)经过改进的SqlCommand公开了一个StatementCompleted事件,可以用来收集这一信息。 StatementCompleted事件的主要参数拥有唯一一个很有意义的属性RecordCount,但是幸运的是,这就是我们要寻找的信息。以下代码段说明可以如何使用这一事件来确定批量操作中各个查询所修改的行数。此代码包括一个处理StatementCompleted事件的过程。如果正在复制和粘贴此代码,则需要单独粘贴该过程。
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
strSQL = "UPDATE Products SET UnitPrice = UnitPrice * 0.85 " +
" WHERE CategoryID = 3;" +
"UPDATE Products SET UnitPrice = UnitPrice * 1.15 " +
" WHERE CategoryID = 4;" +
"UPDATE Products SET UnitPrice = UnitPrice * 0.75 " +
" WHERE CategoryID = 5;";
SqlCommand cmd = new SqlCommand(strSQL, cn);
cmd.StatementCompleted +=
new StatementCompletedEventHandler(HandleStatementCompleted);
int intTotalRowsAffected = cmd.ExecuteNonQuery();
Console.WriteLine("TotalRowsAffected: {0} row(s)", intTotalRowsAffected);
cn.Close();
...
static void HandleStatementCompleted(object sender,
StatementCompletedEventArgs e) {
Console.WriteLine("Statement Affected {0} row(s)", e.RecordCount);
}
1.7 执行获取XML数据的查询
SQL Server支持以XML形式返回数据的查询。如果正在使用这种类型的查询,可以调用SqlCommand的ExecuteXmlReader方法,以通过XmlReader来获得结果,而不是通过SqlDataReader。在第12章中将更详细地研究这一情景。
1.8 在事务中执行查询
SqlCommand类拥有一个 Transaction属性,必须对其进行设置以在SqlTransaction中执行SqlCommand。在上一章中,已经了解了如何使用 SqlConnection类的BeginTransaction方法创建SqlTransaction对象。
如果开始了SqlConnection的一个 SqlTransaction,必须将所有查询与该事务相关联。否则,将会接收到一个InvalidOperationException,同时带有一条消息指出:“当分配给该命令的连接处于挂起的本地事务中时,ExecuteNonQuery要求该命令拥有一个事务。该命令的Transaction属性未被初始化。”
有两种方式可以将SqlCommand与 SqlTransaction关联在一起。可以将SqlCommand的Transaction属性设置为SqlTransaction,也可以将 SqlTransaction传递给SqlCommand的构造函数。以下代码使用第二种方法。在执行查询并确定该查询影响多少行之后,该代码调用 SqlTransaction的Rollback方法,以防止数据库提交这些更改。
string strConn, strSQL;
strConn = @"Data Source=./SQLExpress;" +
"Initial Catalog=Northwind;Trusted_Connection=Yes;";
strSQL = "UPDATE Products SET UnitPrice = UnitPrice * .7 " +
" WHERE CategoryID = 1";
SqlConnection cn = new SqlConnection(strConn);
cn.Open();
using (SqlTransaction txn = cn.BeginTransaction()) {
SqlCommand cmd = new SqlCommand(strSQL, cn, txn);
int intRecordsAffected = cmd.ExecuteNonQuery();
Console.WriteLine("Query affected {0} row(s)", intRecordsAffected);
txn.Rollback();
}
cn.Close();
提示 为了确保没有使事务开放太长时间,请考虑在Using代码块中使用SqlTransaction对象,如以上代码段所示。事务保持开放的时间越长,数据库需要为该事务维护锁的时间也就越长,多位用户尝试锁定相同行的机会也就越大。如果在Using代码块的末尾没有提交或回滚SqlTransaction,则会隐式调用Rollback方法。