資料庫儲存過程 儲存過程詳解
儲存過程(Procedure)可以說是一個記錄集吧,它是由一些T-SQL語句組成的程式碼塊,這些T-SQL語句程式碼像一個方法一樣實現一些功能(對單表或多表的增刪改查),然後再給這個程式碼塊取一個名字,在用到這個功能的時候呼叫他就行了。
儲存過程的好處:
1.由於資料庫執行動作時,是先編譯後執行的。然而儲存過程是一個編譯過的程式碼塊,所以執行效率要比T-SQL語句高。
2.一個儲存過程在程式在網路中互動時可以替代大堆的T-SQL語句,所以也能降低網路的通訊量,提高通訊速率。
3.通過儲存過程能夠使沒有許可權的使用者在控制之下間接地存取資料庫,從而確保資料的安全。
小結:總之儲存過程是好東西,在做專案時屬於必備利器,下面介紹儲存過程的基本語法。
一、儲存過程語法
--------------建立儲存過程----------------- CREATE PROC [ EDURE ] procedure_name [ ; number ] [ { @parameter data_type } [ VARYING ] [ = default ] [ OUTPUT ] ] [ ,...n ] [ WITH { RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ] [ FOR REPLICATION ] AS sql_statement [...n ] --------------呼叫儲存過程----------------- EXECUTE Procedure_name '' --儲存過程如果有引數,後面加引數格式為:@引數名=value,也可直接為引數值value --------------刪除儲存過程----------------- drop procedure procedure_name --在儲存過程中能呼叫另外一個儲存過程,而不能刪除另外一個儲存過程
引數含義:
1.procedure_name :儲存過程的名稱,在前面加#為區域性臨時儲存過程,加##為全域性臨時儲存過程。
2.; number:是可選的整數,用來對同名的過程分組,以便用一條 DROP PROCEDURE 語句即可將同組的過程一起除去。例如,名為 orders 的應用程式使用的過程可以命名為 orderproc;1、orderproc;2 等。DROP PROCEDURE orderproc 語句將除去整個組。如果名稱中包含定界識別符號,則數字不應包含在識別符號中,只應在 procedure_name 前後使用適當的定界符。
[email protected]: 儲存過程的引數。可以有一個或多個。使用者必須在執行過程時提供每個所宣告引數的值(除非定義了該引數的預設值)。儲存過程最多可以有 2.100 個引數。
使用 @ 符號作為第一個字元來指定引數名稱。引數名稱必須符合識別符號的規則。每個過程的引數僅用於該過程本身;相同的引數名稱可以用在其它過程中。預設情況下,引數只能代替常量,而不能用於代替表名、列名或其它資料庫物件的名稱。有關更多資訊,請參見 EXECUTE。
4.data_type:引數的資料型別。所有資料型別(包括 text、ntext 和 image)均可以用作儲存過程的引數。不過,cursor 資料型別只能用於 OUTPUT 引數。如果指定的資料型別為 cursor,也必須同時指定 VARYING 和 OUTPUT 關鍵字。有關 SQL Server 提供的資料型別及其語法的更多資訊,請參見資料型別。
說明 對於可以是 cursor 資料型別的輸出引數,沒有最大數目的限制。
5.VARYING: 指定作為輸出引數支援的結果集(由儲存過程動態構造,內容可以變化)。僅適用於遊標引數。
6.default: 引數的預設值。如果定義了預設值,不必指定該引數的值即可執行過程。預設值必須是常量或 NULL。如果過程將對該引數使用 LIKE 關鍵字,那麼預設值中可以包含萬用字元(%、_、[] 和 [^])。
7.OUTPUT :表明引數是返回引數。該選項的值可以返回給 EXEC[UTE]。使用 OUTPUT 引數可將資訊返回給呼叫過程。Text、ntext 和 image 引數可用作 OUTPUT 引數。使用 OUTPUT 關鍵字的輸出引數可以是遊標佔位符。
8.RECOMPILE: 表明 SQL Server 不會快取該過程的計劃,該過程將在執行時重新編譯。在使用非典型值或臨時值而不希望覆蓋快取在記憶體中的執行計劃時,請使用 RECOMPILE 選項。
9.ENCRYPTION: 表示 SQL Server 加密 syscomments 表中包含 CREATE PROCEDURE 語句文字的條目。使用 ENCRYPTION 可防止將過程作為 SQL Server 複製的一部分發布。 說明 在升級過程中,SQL Server 利用儲存在 syscomments 中的加密註釋來重新建立加密過程。
10.FOR REPLICATION :指定不能在訂閱伺服器上執行為複製建立的儲存過程。.使用 FOR REPLICATION 選項建立的儲存過程可用作儲存過程篩選,且只能在複製過程中執行。本選項不能和 WITH RECOMPILE 選項一起使用。
11.AS :指定過程要執行的操作。
12.sql_statement :過程中要包含的任意數目和型別的 Transact-SQL 語句。但有一些限制。
二、樣例
針對上面的表,我使用儲存過程對它做一些操作:
UserAccount | ||||
UserID | UserName | PassWord | RegisterTime | RegisterIP |
12 | 6 | 6 | 2012-12-31 | 6 |
18 | 5 | 5 | 2013-01-01 | 5 |
19 | 1 | 1 | 2013-01-01 | 1 |
20 | 2 | 2 | 2013-01-01 | 2 |
21 | 3 | 3 | 2013-01-01 | 3 |
22 | 4 | 4 | 2013-01-01 | 4 |
23 | 5 | 5 | 2013-01-01 | 5 |
25 | 7 | 7 | 2013-01-01 | 7 |
26 | 8 | 8 | 2013-01-01 | 8 |
NULL | NULL | NULL | NULL | NULL |
針對上面的表,我使用儲存過程對它做一些操作(以SQL Server為基礎編寫樣例)。
2.1 只返回單一記錄集的儲存過程
-------------建立名為GetUserAccount的儲存過程---------------- create Procedure GetUserAccount as select * from UserAccount go -------------執行上面的儲存過程---------------- exec GetUserAccount
結果:相當於執行 select * from UserAccount 這行程式碼,結果為整個表的資料。
2.2 沒有輸入輸出的儲存過程
-------------建立名為GetUserAccount的儲存過程---------------- create Procedure inUserAccount as insert into UserAccount (UserName,[PassWord],RegisterTime,RegisterIP) values(9,9,'2013-01-02',9) go -------------執行上面的儲存過程---------------- exec inUserAccount
結果:相當於執行 insert into UserAccount (UserName,[PassWord],RegisterTime,RegisterIP) values(9,9,'2013-01-02',9) 這行程式碼。
2.3 有返回值的儲存過程
-------------建立名為GetUserAccount的儲存過程---------------- create Procedure inUserAccountRe as insert into UserAccount (UserName,[PassWord],RegisterTime,RegisterIP) values(10,10,'2013-01-02',10) return @@rowcount go -------------執行上面的儲存過程---------------- exec inUserAccountRe
解釋:這裡的@@rowcount為執行儲存過程影響的行數,執行的結果是不僅插入了一條資料,還返回了一個值即 return value =1 ,這個可以在程式中獲取,稍後在c#呼叫儲存過程中會有說到。
2.4 有輸入引數和輸出引數的儲存過程
-------------建立名為GetUserAccount的儲存過程---------------- create Procedure GetUserAccountRe @UserName nchar(20), @UserID int output as if(@UserName>5) select @UserID=COUNT(*) from UserAccount where UserID>25 else set @UserID=1000 go -------------執行上面的儲存過程---------------- exec GetUserAccountRe '7',null
解釋:@UserName為輸入引數,@UserID為輸出引數。 執行結果為@userID為COOUT(*)即 =1。
2.5 同時具有返回值、輸入引數、輸出引數的儲存過程
-------------建立名為GetUserAccount的儲存過程---------------- create Procedure GetUserAccountRe1 @UserName nchar(20), @UserID int output as if(@UserName>5) select @UserID=COUNT(*) from UserAccount where UserID>25 else set @UserID=1000 return @@rowcount go -------------執行上面的儲存過程---------------- exec GetUserAccountRe1 '7',null
結果:@userID為COOUT(*)即 =1,Retun Value=1。
2.6 同時返回引數和記錄集的儲存過程
-------------建立名為GetUserAccount的儲存過程---------------- create Procedure GetUserAccountRe2 @UserName nchar(20), @UserID int output as if(@UserName>5) select @UserID=COUNT(*) from UserAccount where UserID>25 else set @UserID=1000 select * from UserAccount return @@rowcount go -------------執行上面的儲存過程---------------- exec GetUserAccountRe2 '7',null
結果:返回執行 select * from UserAccount 這句程式碼的結果集,同時@userID為COOUT(*)即 =1,Retun Value=9。
2.7 返回多個記錄集的儲存過程
------------建立名為GetUserAccount的儲存過程---------------- create Procedure GetUserAccountRe3 as select * from UserAccount select * from UserAccount where UserID>5 go -------------執行上面的儲存過程---------------- exec GetUserAccountRe3
結果:返回兩個結果集,一個為 select * from UserAccount,另一個為 select * from UserAccount where UserID>5 。
附帶了一些SQL中的一些全域性變數的查詢語句:
select APP_NAME ( ) as w --當前會話的應用程式 select @@IDENTITY --返回最後插入的標識值 select USER_NAME() --返回使用者資料庫使用者名稱 SELECT @@CONNECTIONS --返回自上次SQL啟動以來連線或試圖連線的次數。 SELECT GETDATE() --當前時間 SELECT @@CPU_BUSY/100 --返回自上次啟動SQL 以來 CPU 的工作時間,單位為毫秒 USE tempdb SELECT @@DBTS as w --為當前資料庫返回當前 timestamp 資料型別的值。這一 timestamp 值保證在資料庫中是唯一的。 select @@IDENTITY as w --返回最後插入的標識值 SELECT @@IDLE as w --返回SQL自上次啟動後閒置的時間,單位為毫秒 SELECT @@IO_BUSY AS w --返回SQL自上次啟動後用於執行輸入和輸出操作的時間,單位為毫秒 SELECT @@LANGID AS w --返回當前所使用語言的本地語言識別符號(ID)。 SELECT @@LANGUAGE AS w --返回當前使用的語言名 SELECT @@LOCK_TIMEOUT as w --當前會話的當前鎖超時設定,單位為毫秒。 SELECT @@MAX_CONNECTIONS as w --返回SQL上允許的同時使用者連線的最大數。返回的數不必為當前配置的數值 EXEC sp_configure --顯示當前伺服器的全域性配置設定 SELECT @@MAX_PRECISION as w --返回 decimal 和 numeric 資料型別所用的精度級別,即該伺服器中當前設定的精度。預設最大精度38。 select @@OPTIONS as w --返回當前 SET 選項的資訊。 SELECT @@PACK_RECEIVED as w --返回SQL自啟動後從網路上讀取的輸入資料包數目。 SELECT @@PACK_SENT as w --返回SQ自上次啟動後寫到網路上的輸出資料包數目。 SELECT @@PACKET_ERRORS as w --返回自SQL啟動後,在SQL連線上發生的網路資料包錯誤數。 SELECT @@SERVERNAME as w --返回執行SQL伺服器名稱。 SELECT @@SERVICENAME as w --返回SQL正在其下執行的登錄檔鍵名 SELECT @@TIMETICKS as w --返回SQL伺服器一刻度的微秒數 SELECT @@TOTAL_ERRORS AS w --返回 SQL伺服器自啟動後,所遇到的磁碟讀/寫錯誤數。 SELECT @@TOTAL_READ as w --返回 SQL伺服器自啟動後讀取磁碟的次數。 SELECT @@TOTAL_WRITE as w --返回SQL伺服器自啟動後寫入磁碟的次數。 SELECT @@TRANCOUNT as w --返回當前連線的活動事務數。 SELECT @@VERSION as w --返回SQL伺服器安裝的日期、版本和處理器型別。SQLServer資料庫的一些全域性變數
小結:上面我們建立了各式的儲存過程,下面看我們在c#中怎樣呼叫這些儲存過程。
三、c#呼叫儲存過程
呼叫以上寫的各種型別的儲存過程的C#程式碼
public partial class ProcedureTest : System.Web.UI.Page { public static string conn = ConfigurationManager.ConnectionStrings["StuRelationDBConnectionString"].ConnectionString; public SqlConnection con = new SqlConnection(conn); protected void Page_Load(object sender, EventArgs e) { runGetUserAccountRe3(); } //只返回單一記錄集的儲存過程GetUserAccount public void runGetUserAccount() { SqlDataAdapter dp = new SqlDataAdapter(common("GetUserAccount")); DataSet ds = new DataSet(); // 填充dataset dp.Fill(ds); rpt.DataSource = ds; rpt.DataBind(); } //沒有輸入輸出的儲存過程inUserAccount public void runinUserAccount() { con.Open(); Label1.Text = common("inUserAccount").ExecuteNonQuery().ToString(); con.Close(); } //有返回值的儲存過程inUserAccountRe public void runinUserAccountRe() { // 建立引數 SqlCommand cmd = common("inUserAccountRe"); IDataParameter[] parameters = { new SqlParameter("rval", SqlDbType.Int,4) }; // 將引數型別設定為 返回值型別 parameters[0].Direction = ParameterDirection.ReturnValue; // 新增引數 cmd.Parameters.Add(parameters[0]); con.Open(); // 執行儲存過程並返回影響的行數 Label1.Text = cmd.ExecuteNonQuery().ToString(); con.Close(); // 顯示影響的行數和返回值 Label1.Text += "-" + parameters[0].Value.ToString(); } //有輸入引數和輸出引數的儲存過程 public void runGetUserAccountRe() { SqlCommand cmd = common("GetUserAccountRe"); // 建立引數 IDataParameter[] parameters = { new SqlParameter("@UserName", SqlDbType.NChar,20) , new SqlParameter("@UserID", SqlDbType.Int) , }; // 設定引數型別 parameters[0].Value = "7"; parameters[1].Direction = ParameterDirection.Output; // 設定為輸出引數 // 新增引數 cmd.Parameters.Add(parameters[0]); cmd.Parameters.Add(parameters[1]); con.Open(); // 執行儲存過程並返回影響的行數 Label1.Text = cmd.ExecuteNonQuery().ToString(); con.Close(); // 顯示影響的行數和輸出引數 Label1.Text += "-" + parameters[1].Value.ToString(); } //同時具有返回值、輸入引數、輸出引數的儲存過程GetUserAccountRe1 public void runGetUserAccountRe1() { SqlCommand cmd = common("GetUserAccountRe1"); // 建立引數 IDataParameter[] parameters = { new SqlParameter("@UserName", SqlDbType.NChar,20) , new SqlParameter("@UserID", SqlDbType.Int) , new SqlParameter("rval", SqlDbType.Int,4) }; // 設定引數型別 parameters[0].Value = "7"; parameters[1].Direction = ParameterDirection.Output; // 設定為輸出引數 parameters[2].Direction = ParameterDirection.ReturnValue; //設定為返回值 // 新增引數 cmd.Parameters.Add(parameters[0]); cmd.Parameters.Add(parameters[1]); cmd.Parameters.Add(parameters[2]); con.Open(); // 執行儲存過程並返回影響的行數 Label1.Text = cmd.ExecuteNonQuery().ToString(); con.Close(); // 顯示影響的行數和輸出引數 Label1.Text += "-輸出引數為:" + parameters[1].Value.ToString(); Label1.Text += "-返回值為:" + parameters[2].Value.ToString(); } //同時返回引數和記錄集的儲存過程GetUserAccountRe2 public void runGetUserAccountRe2() { SqlCommand cmd = common("GetUserAccountRe2"); // 建立引數 IDataParameter[] parameters = { new SqlParameter("@UserName", SqlDbType.NChar,20) , new SqlParameter("@UserID", SqlDbType.Int) , new SqlParameter("rval", SqlDbType.Int,4) }; // 設定引數型別 parameters[0].Value = "7"; parameters[1].Direction = ParameterDirection.Output; // 設定為輸出引數 parameters[2].Direction = ParameterDirection.ReturnValue; //設定為返回值 // 新增引數 cmd.Parameters.Add(parameters[0]); cmd.Parameters.Add(parameters[1]); cmd.Parameters.Add(parameters[2]); con.Open(); // 執行儲存過程並返回影響的行數 Label1.Text = cmd.ExecuteNonQuery().ToString(); DataSet ds = new DataSet(); SqlDataAdapter dt = new SqlDataAdapter(cmd); dt.Fill(ds); rpt.DataSource = ds; rpt.DataBind(); con.Close(); // 顯示影響的行數和輸出引數 Label1.Text += "-輸出引數為:" + parameters[1].Value.ToString(); Label1.Text += "-返回值為:" + parameters[2].Value.ToString(); } //返回多個記錄集的儲存過程 public void runGetUserAccountRe3() { DataSet ds = new DataSet(); SqlDataAdapter dt = new SqlDataAdapter(common("GetUserAccountRe3")); dt.Fill(ds); rpt1.DataSource = ds.Tables[0].DefaultView; rpt1.DataBind(); rpt2.DataSource = ds.Tables[1].DefaultView; rpt2.DataBind(); } public SqlCommand common(string proName) { SqlCommand cmd = new SqlCommand(); // 設定sql連線 cmd.Connection = con; // 如果執行語句 cmd.CommandText = proName; // 指定執行語句為儲存過程 cmd.CommandType = CommandType.StoredProcedure; return cmd; } }Demos