本文介紹了幾種如何使用一個SqlCommand執行多條SQL語句的技術。sql
使用ADO.NET對SQL Server進行數據存儲常常被忽略的功能之一是它可以使用單個語句執行多個SQL語句SqlCommand。一般,程序分別執行語句和/或調用存儲過程來執行更大的語句。固然,使用存儲過程是一種首選方法,可是在某些狀況下,一次調用執行多個語句是有益的。這可使用批處理來完成,這基本上意味着一組SQL或T-SQL語句在一塊兒。數據庫
爲了測試功能,讓咱們有一張數據庫表。ide
CREATE TABLE MultiStatementTest ( id int not null identity(1,1), somevalue int not null );
並用幾行填充它。oop
DECLARE @counter int = 1 BEGIN WHILE (@counter <= 5) BEGIN INSERT INTO MultiStatementTest (somevalue) VALUES (RAND() * 1000); SET @counter = @counter + 1; END; END;
如今數據看起來像:測試
SELECT * FROM MultiStatementTest;
id somevalueui
1 854
2 73
3 732
4 546
5 267翻譯
該測試程序易於使用。只需爲建立測試表的數據庫定義正確的鏈接字符串,便可開始運行測試。code
第一個變體用於SqlCommand.ExecuteNonQuery對測試表執行兩個單獨的SQL語句。第一個將字段更新somevalue一個,第二個更新字段 。該方法以下所示:orm
/// <summary> /// Executes two separate updates against the the connection /// </summary> /// <param name="connectionString">Connection string to use</param> /// <param name="generateError">Should the statement generate an error</param> /// <returns>True if succesful</returns> public static bool ExecuteMultipleUpdates(string connectionString, bool generateError = false) { System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(); System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(); int rowsAffected; connection.ConnectionString = connectionString; command.CommandText = @" UPDATE MultiStatementTest SET somevalue = somevalue + 1; UPDATE MultiStatementTest SET" + (generateError ? "WONTWORK" : "") + " somevalue = somevalue + 2;"; command.CommandType = System.Data.CommandType.Text; command.Connection = connection; try { connection.Open(); rowsAffected = command.ExecuteNonQuery(); } catch (System.Exception exception) { System.Windows.MessageBox.Show(exception.Message, "Error occurred"); return false; } finally { command.Dispose(); connection.Dispose(); } System.Windows.MessageBox.Show(string.Format("{0} rows updated", rowsAffected, "Operation succesful")); return true; }
所以,該CommandText屬性包含將在此批處理中執行的全部語句。語句用分號分隔。對象
執行批處理後,行已更新兩次,所以表的內容相似於:
1857
2 76
3735
4549
5270
須要注意的重要一件事是,返回的受影響的行數ExecuteNonQuery爲10。表中有五行,每行更新了兩次,所以更新的總數爲10。所以,即便有批次,也能夠檢查不管哪一個語句進行更新,都將更新正確的行數。
下一個測試是執行兩個不一樣的SELECT語句,並使用一個SqlDataReader類讀取結果。方法是:
/// <summary> /// Executes two separate select statements against the the connection using data reader /// </summary> /// <param name="connectionString">Connection string to use</param> /// <param name="generateError">Should the statement generate an error</param> /// <returns>True if succesful</returns> public static bool ExecuteReader(string connectionString, bool generateError = false) { System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(); System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(); System.Data.SqlClient.SqlDataReader dataReader; System.Text.StringBuilder stringBuilder; bool loopResult = true; connection.ConnectionString = connectionString; command = new System.Data.SqlClient.SqlCommand(); command.CommandText = @" SELECT somevalue FROM MultiStatementTest WHERE somevalue%2 = 1; SELECT somevalue FROM MultiStatementTest " + (generateError ? "WONTWORK" : "WHERE") + " somevalue%2 = 0;"; command.CommandType = System.Data.CommandType.Text; command.Connection = connection; try { connection.Open(); dataReader = command.ExecuteReader(); while (loopResult) { stringBuilder = new System.Text.StringBuilder(); while (dataReader.Read()) { stringBuilder.AppendLine(dataReader.GetInt32(0).ToString()); } System.Windows.MessageBox.Show(stringBuilder.ToString(), "Data from the result set"); loopResult = dataReader.NextResult(); } } catch (System.Exception exception) { System.Windows.MessageBox.Show(exception.Message, "Error occurred"); return false; } finally { command.Dispose(); connection.Dispose(); } return true; }
批處理中的想法是相同的,兩個語句之間用分號分隔。在此示例中,將行分爲兩個結果集,具體取決於數字是奇數仍是偶數。當ExecuteReader被調用時,第一個結果集是自動可用。該方法遍歷各行並顯示結果:
857
735
549
爲了得到下一個結果,必須指示讀者使用該NextResult方法前進到下一個結果集。此後,第二組值能夠再次循環經過。第二組結果:
76
270
SqlDataReader若是要將結果存儲在中,一般使用DataSet。對於下一個測試,讓咱們使用SqlDataAdapter 來填充數據集。代碼以下:
/// <summary> /// Executes two separate select statements against the the connection /// </summary> /// <param name="connectionString">Connection string to use</param> /// <param name="generateError">Should the statement generate an error</param> /// <returns>True if succesful</returns> public static bool ExecuteMultipleSelects(string connectionString, bool generateError = false) { System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(); System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(); System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter(); System.Data.DataSet dataset = new System.Data.DataSet(); connection.ConnectionString = connectionString; command = new System.Data.SqlClient.SqlCommand(); command.CommandText = @" SELECT * FROM MultiStatementTest WHERE somevalue%2 = 1; SELECT " + (generateError ? "WONTWORK" : "*") + " FROM MultiStatementTest WHERE somevalue%2 = 0;"; command.CommandType = System.Data.CommandType.Text; command.Connection = connection; try { connection.Open(); adapter.SelectCommand = command; adapter.Fill(dataset); } catch (System.Exception exception) { System.Windows.MessageBox.Show(exception.Message, "Error occurred"); return false; } finally { command.Dispose(); connection.Dispose(); } System.Windows.MessageBox.Show(string.Format( "Dataset contains {0} tables, {1} rows in table 1 and {2} rows in table 2", dataset.Tables.Count, dataset.Tables[0].Rows.Count, dataset.Tables[1].Rows.Count, "Operation succesful")); return true; }
如今,以這種方式獲取數據很是容易。該代碼僅調用Fill適配器的方法,並將DataSet參數做爲參數傳遞。適配器會自動DataTable在數據集中建立兩個單獨的對象,而後填充它們。在個人測試場景中,第一張表包含三行,第二張表包含兩行。
因爲在此示例中,表是即時建立的,所以會自動爲其命名Table1,Table2所以,若是使用名稱來引用表,則將名稱更改成更具描述性的名稱是明智的。
儘管存儲過程很是出色,可是有時T-SQL代碼在本質上可能很是動態。在這種狀況下,可能很難建立存儲過程。批處理還能夠用於執行一堆T-SQL語句。在這種方法中,數據庫中沒有命名對象,但批處理的執行方式與原本的執行方式相同,例如,能夠從SQL Server Management Studio中執行。
測試代碼以下:
/// <summary> /// Executes an anonymous T-SQL batch against the the connection /// </summary> /// <param name="connectionString">Connection string to use</param> /// <param name="generateError">Should the statement generate an error</param> /// <returns>True if succesful</returns> public static bool ExecuteAnonymousTSql(string connectionString, bool generateError = false) { System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(); System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(); int rowsAffected; connection.ConnectionString = connectionString; command.CommandText = @" DECLARE @counter int = 1 BEGIN WHILE (@counter <= 5) BEGIN INSERT INTO MultiStatementTest (somevalue) VALUES (RAND() * 100000); SET @counter = @counter + 1; " + (generateError ? "WONTWORK" : "") + @" END; END;"; command.CommandType = System.Data.CommandType.Text; command.Connection = connection; try { connection.Open(); rowsAffected = command.ExecuteNonQuery(); } catch (System.Exception exception) { System.Windows.MessageBox.Show(exception.Message, "Error occurred"); return false; } finally { command.Dispose(); connection.Dispose(); } System.Windows.MessageBox.Show(string.Format("{0} rows inserted", rowsAffected, "Operation succesful")); return true; }
如今,在此示例中,使用了與開始建立幾個測試行相同的腳本。如您所見,變量聲明,循環等是包含在批處理中的徹底有效的語句。
運行該命令時,會將另外五行添加到表中。還要注意,因爲NOCOUNT默認狀況下處於關閉狀態,所以該ExecuteNonQuery方法將返回正確數量的行插入到批處理中。若是NOCOUNT設置爲on,則受影響的行數爲-1。
若是在批處理中執行單個語句時發生錯誤怎麼辦?我使用了一些語法錯誤來對此進行測試。該批處理將做爲一個總體進行分析,所以即便之後的語句包含語法錯誤,該批處理也不會起做用。例如,若是UPDATE批處理中包含錯誤的 語句,則表的狀態不會更改。
若是錯誤不是語法錯誤而是在執行過程當中發生,則狀況會有所不一樣。考慮例如當錯誤值出現時檢測到的外鍵錯誤。在這種狀況下,先前的語句可能已經更改了數據庫狀態(取決於語句),所以建議像往常同樣使用適當的事務。
雖然批處理的方式不會(也不該該)代替良好的舊存儲過程等,但若是使用得當,它們將頗有用。只要調用之間不須要客戶端邏輯,它們就能夠用於建立很是動態的操做,而無需進行屢次往返。
本文翻譯自文章: Executing multiple SQL statements as one against SQL Server請添加連接描述