存儲過程(stored procedure)有時也稱sproc,它是真正的腳本,更準確地說,它是批處理(batch),但都不是很確切,它存儲與數據庫而不是單獨的文件中。
存儲過程中有輸入參數,輸出參數以及返回值等。
一、創建存儲過程
創建存儲過程的方法和創建數據庫中任何其他對象一樣,除了他使用AS關鍵字外。存儲過程的基本語法如下:
CREATEPROCEDURE|PROC<sproc name>[ [schema.]<data type>[VARYING][=][OUT[PUT]] [READONLY][, [schema.]<data type>[VARYING][=][OUT[PUT]] [READONLY][,... ... ]] [WITH RECOMPILE | ENCRYPTION | [EXECUTE AS { CALLER | SELF | OWNER | <'user name'>}]AS<code>| EXTERNAL NAME <assembly name>.<assembly class>.<method>
在語法中,PROC是PROCEDURE的縮寫,兩個選項的意思一樣。
在對存儲過程命名完之後,接着是參數列表。參數是可選的。
關鍵字AS其後就是實際的代碼。
簡單的存儲過程示例:
CREATEPROC spPerson ASSELECT*FROM Person
執行存儲過程:
EXEC spPerson
查看結果:
二、ALTER修改存儲過程
ALTER PROC和CREATE PROC的區別如下:
ALTER PROC期望找到一個已有的存儲過程,而CREATE則不是。
ALTER PROC保留了存儲過程上已經建立的任何權限。它在系統對象中保留了對象Id並允許保留依賴關係。例如,如果過程A調用過程B,如果刪除並重建B,那麼就不能在看到這兩者間的依賴關係。如果使用ALTER,則依賴關係仍然存在。
ALTER PROC在可能調用被修改的存儲過程的其他對象上保留了任何依賴信息。
示例:
ALTERPROC spPerson ASSELECT*FROM Person WHERE Id =45
三、刪除存儲過程
刪除存儲過程的語法最簡單:
DROPPROC|PROCEDURE<sproc name>[;]
這樣就完成了存儲過程的刪除。
四、參數化
如果存儲過程沒有辦法接受一些數據,告訴其要完成的任務,則在大多數情況下,存儲過程不會有太大幫助。例如,要刪除一條數據,但卻不指定Id,則存儲過程也不知道要刪除哪條,所以使用輸入參數非常有必要。
1、聲明參數
聲明參數需要以下2到4部分的信息:
名稱
數據類型
默認值
方向
其語法如下所示:
@parameter_name[AS] datatype [=default|NULL][VARYING][OUTPUT | OUT]
對於名稱,有一組簡單的規則。首先,它必須以@符號(和變量一樣)開始。此外,除了不能內嵌空格外,其規則與普通變量規則相同。
數據類型和名稱一樣,必須像變量那樣聲明,採用SQL Server內置的或用戶自定義的數據類型。
聲明需要類型時需要注意,當聲明CURSOR類型參數時,必須也使用VARYING和OUTPUT選項。同時,OUTPUT可以簡寫爲OUT。
在默認值方面,參數與變量不同。對於同樣的情況,變量一般初始化爲NULL值,而參數不是。事實上,如果不提供默認則,則會假設參數是必須的,並且當調用存儲過程時需要提供一個初始值。
一個需要傳入參數的存儲過程示例:
CREATEPROC spName @Namenvarchar(50) ASSELECT Name FROM Person WHERE Name LIKE@Name+'%';
執行存儲過程:
EXEC spName '酒';
顯示結果如下:
2、提供默認值
爲了使參數是可選的,必須提供默認值。方法是在數據類型後在逗號之前添加"="符號和作爲默認值的值。這樣,存儲過程的用戶尅有決定對此參數不提供值或是提供他們自己的值。
創建一個存儲過程如下:
CREATEPROC spName @Namenvarchar(50) =NULLASIF@NameISNOTNULLSELECT*FROM Person WHERE NAME =@NameELSESELECT*FROM Person WHERE Id =45
執行如下語句:
EXEC spName EXEC spName '如意刀狼'
輸出結果如下:
3、輸出參數
下面來看看一個獲得OUTPUT參數的存儲過程:
CREATEPROC InsertPerson @Idint OUTPUT --必須註明爲OUTPUTASINSERTINTO Person VALUES('劉備',22,190,'不詳','未婚','幼兒園','不詳',4999999) SET@Id=@@IDENTITY
執行存儲過程:
DECLARE@Idint --實際上,調用時名稱可以不同,例如也可以爲@Num,@i等等。EXEC InsertPerson @Id OUTPUT --注意此處也要有OUTPUTSELECT@Id
顯示結果如下:
對於存儲過程本身以及調用腳本對它的使用,需要注意以下幾點:
對於存儲過程聲明中的輸出參數,需要使用OUTPUT關鍵字。
和聲明存儲過程時一樣,調用存儲過程時,必須使用OUTPUT關鍵字。這樣就對SQL Server作了提前通知,告訴它參數所需要的特殊處理。但需要注意的是,如果忘記包含OUTPUT關鍵字,不會產生運行時錯誤,但是輸出的值不會傳入變量中(變量很可能是NULL)。
賦值給輸出結果的變量不需要和存儲過程中的內部參數擁有相同的名稱。
EXEC(或EXECUTE)關鍵字是必須的,因爲對存儲過程的調用並不是批處理要做的第一件事(如果存儲過程的調用是批處理的第一件事,則可以不使用EXEC)。
五、返回值
返回值的用途非常廣泛,例如,返回數據,標識值或是存儲過程影響的行數等等。而其實際作用是返回值可用來確定存儲過程執行的狀態。
事實上,不管是否提供返回值,程序都會收到一個返回值。SQL Server默認會在完成存儲過程時自動返回一個0值。
爲了從存儲過程向調用代碼傳遞返回值,只需要使用RETURN語句。
RETURN[]
要特別注意的是:返回值必須是整數。
關於RETURN語句,最重要的是知道它是無條件地從存儲過程中退出的。無論運行到存儲過程的哪個位置,在調用RETURN語句之後將不會執行任何一行代碼。
這裏的無條件,並不是說無論執行到代碼的何處都將執行RETURN語句。相反,可以再存儲過程中有多個RETURN語句。只有當代碼的標準條件結構發出命令的時候,纔會執行這些RETURN語句。一旦發生,就不能再退回了。
創建一個存儲過程如下:
CREATEPROC spTestReturns ASDECLARE@MyMessagenvarchar(50); DECLARE@MyOtherMessagenvarchar(50); SELECT@MyMessage='第一個RETURN'; PRINT@MyMessage; RETURN; SELECT@MyOtherMessage='第二個RETURN'; PRINT@MyOtherMessage; RETURN;
執行存儲過程,輸出如下:
爲了能捕獲RETURN語句的值,需要在EXEC語句中把值賦給變量。例如:
DECLARE@Returnint EXEC@Return= spTestReturns SELECT@Return
輸出如下:
雖然簡單但是還不錯。當運行時,可以看到RETURN語句卻是在運行其他代碼前終止了代碼運行。
如果總是返回0,那麼執行成不成功都不知道,那麼我們現在來改寫下上面的存儲過程,讓其返回一個指定的值,以指示執行狀態。
CREATEPROC spTestReturns ASDECLARE@MyMessagenvarchar(50); DECLARE@MyOtherMessagenvarchar(50); SELECT@MyMessage='第一個RETURN'; PRINT@MyMessage; RETURN100; --將這裏改成返回100SELECT@MyOtherMessage='第二個RETURN'; PRINT@MyOtherMessage; RETURN;
執行之後,顯示結果如下:
六、存儲過程的優缺點
存儲過程的主要優點包括以下幾個方面:
使得需要過程式動作的進程可調用
安全性
性能
1、創建可調用的進程
很多人並沒有意識到要充分使用存儲過程,使其作爲實現安全性的工具。和視圖類似,可以創建一個返回記錄集的存儲過程而不用賦予用戶訪問底層數據表的權限。賦予某人執行一個存儲過程的權限意味着他們可以在該存儲過程中執行任何動作。不過要假設動作是在存儲過程的上下文中執行的。
2、存儲過程和性能
一般來說,存儲過程有助於系統性能的提高。但是,如果設計的存儲過程缺乏只能,那麼它會使在其創建的進程變得非常緩慢。
存儲過程的運行示意圖如下:
首先運行CREATE PROC過程。這回解析查詢以確保會實際運行這些代碼。它與直接運行腳本的區別在於CREATE PROC命令可以利用所謂的延遲名稱解析。延遲名稱解析可以忽略一些對象還不存在的事實。
在創建了存儲過程後,它將等待第一次執行。在那時,存儲過程被優化,而查詢計劃被編譯並且緩存到系統上。後續幾次運行該存儲過程時,除非通過使用WITH RECOMPILE選項指定,否則都會使用緩存的查詢計劃而不是創建一個新的查詢計劃。這意味着每次使用該存儲過程時,存儲過程都會跳過很多優化和編譯工作。節省的確切時間取決於批處理的複雜性,批處理中表的大小,以及每個表上索引的數量。通常,節省的時間不是很多。但對於大多數場景來說可能是1秒或更少-但通過百分比可以計算出此區別(1秒比2秒快了100%)。當需要進行多次調用時或針對循環的情況,這一區別會變得更明顯。
3、存儲過程的不利方面
對於存儲過程的不利之處要認識到的最重要的一點事,除非手動地干預(使用WITH RECOMPILE選項),否則只會在第一次運行存儲過程的時候,或者當查詢所涉及的表更新了統計信息時,纔對存儲過程進行優化。
這種"一次優化,多次使用"的策略節省了存儲過程的時間,但是該策略也是一把雙刃劍。如果查詢是動態的(即是在使用EXEC命令時建立的),那麼只會在第一次運行時對存儲過程進行優化,但是會發現以後再也不這樣了。簡而言之,可能會使用錯誤的計劃。
4、WITH RECOMPILE選項
可以利用存儲過程提供的安全性代碼和代碼封裝方面的好處,但還是忽略了預編譯代碼方面的影響。可以迴避未使用正確的查詢計劃的問題,因爲可以確保爲特定一次運行創建新的計劃。方法就是使用WITH RECOMPILE選項。
使用該選項的方式有兩種:
1、可以在運行時包含WITH RECOMPILE。
EXEC spMySproc '1/1/2004'WITH RECOMPILE
這告訴SQL Server拋棄已有的執行計劃並且創建一個新的計劃-但只是這一次。也就是說,只是這次使用WITH RECOMPILE選項來執行存儲過程。
也可以通過在存儲過程中包含WITH RECOMPILE選項來使之變得更持久。如果使用這種方式,則在CREATE PROC或ALTER PROC語句中的AS語句前添加WITH RECOMPILE選項即可。
如果通過該選項創建存儲過程,那麼無論在運行時選擇了其他什麼選項,每次運行存儲過程都會重新編譯它。