T-SQL 之 存储过程
當存儲過程執行一次后,可以將語句緩存中,這樣下次執行的時候直接使用緩存中的語句。這樣就可以提高存儲過程的性能。
一、存儲過程的概念
存儲過程Procedure是一組為了完成特定功能的SQL語句集合,經編譯后存儲在數據庫中,用戶通過指定存儲過程的名稱并給出參數來執行。
存儲過程中可以包含邏輯控制語句和數據操縱語句,它可以接受參數、輸出參數、返回單個或多個結果集以及返回值。
由于存儲過程在創建時即在數據庫服務器上進行了編譯并存儲在數據庫中,所以存儲過程運行要比單個的SQL語句塊要快。同時由于在調用時只需用提供存儲過程名和必要的參數信息,所以在一定程度上也可以減少網絡流量、簡單網絡負擔。
1、 存儲過程的優點
[1] 存儲過程允許標準組件式編程
存儲過程創建后可以在程序中被多次調用執行,而不必重新編寫該存儲過程的SQL語句。而且數據庫專業人員可以隨時對存儲過程進行修改,但對應用程序源代碼卻毫無影響,從而極大的提高了程序的可移植性。
[2] 存儲過程能夠實現較快的執行速度
如果某一操作包含大量的T-SQL語句代碼,分別被多次執行,那么存儲過程要比批處理的執行速度快得多。因為存儲過程是預編譯的,在首次運行一個存儲過程時,查詢優化器對其進行分析、優化,并給出最終被存在系統表中的存儲計劃。而批處理的T-SQL語句每次運行都需要預編譯和優化,所以速度就要慢一些。
[3] 存儲過程減輕網絡流量
對于同一個針對數據庫對象的操作,如果這一操作所涉及到的T-SQL語句被組織成一存儲過程,那么當在客戶機上調用該存儲過程時,網絡中傳遞的只是該調用語句,否則將會是多條SQL語句。從而減輕了網絡流量,降低了網絡負載。
[4] 存儲過程可被作為一種安全機制來充分利用
系統管理員可以對執行的某一個存儲過程進行權限限制,從而能夠實現對某些數據訪問的限制,避免非授權用戶對數據的訪問,保證數據的安全。
二、創建存儲過程的基本語法
---------------------------創建存儲過程-----------------------
CREATE PROC[ EDURE ] procedure_name [ ; number ]
[ { @Parameter Datatype }[ VARYING ] [ = default ] [|OUT|OUTPUT]]
......
[ WITH { RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ]
[ FOR REPLICATION ]
AS
BEGIN
sql_statement [ ...n ]
END
--------------調用存儲過程-----------------
--存儲過程如果有參數,后面加參數格式為:@參數名=value,也可直接為參數值value
EXECUTE Procedure_name
--------------刪除存儲過程-----------------
--在存儲過程中能調用另外一個存儲過程,而不能刪除另外一個存儲過程
IF (EXISTS (SELECT * FROM sys.objects WHERE name = 'procedure_name'))
DROP PROCEDURE procedure_name
創建存儲過程的參數:
1、procedure_name :存儲過程的名稱,在前面加#為局部臨時存儲過程,加##為全局臨時存儲過程。
2、; number:是可選的整數,用來對同名的過程分組,以便用一條 DROP PROCEDURE 語句即可將同組的過程一起除去。例如,名為 orders 的應用程序使用的過程可以命名為 orderproc;1、orderproc;2 等。DROP PROCEDURE orderproc 語句將除去整個組。如果名稱中包含定界標識符,則數字不應包含在標識符中,只應在 procedure_name 前后使用適當的定界符。
3、@Parameter: 存儲過程的參數??梢杂?-多個。用戶必須在執行過程時提供每個所聲明參數的值(除非定義了該參數的默認值)。存儲過程最多可以有 100 個參數。
使用 @ 符號作為第一個字符來指定參數名稱。參數名稱必須符合標識符的規則。每個過程的參數僅用于該過程本身;相同的參數名稱可以用在其它過程中。默認情況下,參數只能代替常量,而不能用于代替表名、列名或其它數據庫對象的名稱。有關更多信息,請參見 EXECUTE。
4、Datatype:參數的數據類型。所有數據類型(包括 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 語句。但有一些限制。
二、ALTER修改存儲過程
ALTER PROC和CREATE PROC的區別如下:
[1] ALTER PROC期望找到一個已有的存儲過程,而CREATE則不是。
[2] ALTER PROC保留了存儲過程上已經建立的任何權限。它在系統對象中保留了對象Id并允許保留依賴關系。例如,如果過程A調用過程B,如果刪除并重建B,那么就不能在看到這兩者間的依賴關系。如果使用ALTER,則依賴關系仍然存在。
[3] ALTER PROC在可能調用被修改的存儲過程的其他對象上保留了任何依賴信息。
示例:
ALTER PROC spPerson AS SELECT * FROM Person WHERE Id = 45
三、刪除存儲過程
刪除存儲過程的語法最簡單:
DROP PROC|PROCEDURE <sproc name>[;]
四、參數化
如果存儲過程沒有辦法接受一些數據,告訴其要完成的任務,則在大多數情況下,存儲過程不會有太大幫助。例如,要刪除一條數據,但卻不指定Id,則存儲過程也不知道要刪除哪條,所以使用輸入參數非常有必要。
1、聲明參數
語法如下:
@parameter_name [AS] datatype [=default|NULL] [VARYING] [OUTPUT | OUT]
參數名稱,有一組簡單的規則。首先,它必須以@符號(和變量一樣)開始。此外,除了不能內嵌空格外,其規則與普通變量規則相同。
數據類型和名稱一樣,必須像變量那樣聲明,采用SQL Server內置的或用戶自定義的數據類型。
聲明需要類型時需要注意,當聲明CURSOR類型參數時,必須也使用VARYING和OUTPUT選項。同時,OUTPUT可以簡寫為OUT。
在默認值方面,參數與變量不同。對于同樣的情況,變量一般初始化為NULL值,而參數不是。事實上,如果不提供默認則,則會假設參數是必須的,并且當調用存儲過程時需要提供一個初始值。
五、返回值
返回值的用途非常廣泛,例如,返回數據,標識值或是存儲過程影響的行數等等。而其實際作用是返回值可用來確定存儲過程執行的狀態。
事實上,不管是否提供返回值,程序都會收到一個返回值。SQL Server默認會在完成存儲過程時自動返回一個0值。
為了從存儲過程向調用代碼傳遞返回值,只需要使用RETURN語句。
RETURN [<integer value to return>]
要特別注意的是:返回值必須是整數。
關于RETURN語句,最重要的是知道它是無條件地從存儲過程中退出的。無論運行到存儲過程的哪個位置,在調用RETURN語句之后將不會執行任何一行代碼。
這里的無條件,并不是說無論執行到代碼的何處都將執行RETURN語句。相反,可以再存儲過程中有多個RETURN語句。只有當代碼的標準條件結構發出命令的時候,才會執行這些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選項即可。
如果通過該選項創建存儲過程,那么無論在運行時選擇了其他什么選項,每次運行存儲過程都會重新編譯它。
三、幾種特殊存儲過程
1、 不緩存存儲過程
--WITH RECOMPILE 不緩存
if (object_id('proc_temp', 'P') is not null)
drop proc proc_temp
go
create proc proc_temp
with recompile
as
select * from student;
go
exec proc_temp;
2、 加密存儲過程
--加密WITH ENCRYPTION
if (object_id('proc_temp_encryption', 'P') is not null)
drop proc proc_temp_encryption
go
create proc proc_temp_encryption
with encryption
as
select * from student;
go
exec proc_temp_encryption;
exec sp_helptext 'proc_temp';
exec sp_helptext 'proc_temp_encryption';
3、 帶游標參數存儲過程
if (object_id('proc_cursor', 'P') is not null)
drop proc proc_cursor
go
create proc proc_cursor
@cur cursor varying output
as
set @cur = cursor forward_only static for
select id, name, age from student;
open @cur;
go
--調用
declare @exec_cur cursor;
declare @id int,
@name varchar(20),
@age int;
exec proc_cursor @cur = @exec_cur output;--調用存儲過程
fetch next from @exec_cur into @id, @name, @age;
while (@@fetch_status = 0)
begin
fetch next from @exec_cur into @id, @name, @age;
print 'id: ' + convert(varchar, @id) + ', name: ' + @name + ', age: ' + convert(char, @age);
end
close @exec_cur;
deallocate @exec_cur;--刪除游標
4、 分頁存儲過程
---存儲過程、row_number完成分頁
if (object_id('pro_page', 'P') is not null)
drop proc proc_cursor
go
create proc pro_page
@startIndex int,
@endIndex int
as
select count(*) from product
;
select * from (
select row_number() over(order by pid) as rowId, * from product
) temp
where temp.rowId between @startIndex and @endIndex
go
--drop proc pro_page
exec pro_page 1, 4
--
--分頁存儲過程
if (object_id('pro_page', 'P') is not null)
drop proc pro_stu
go
create procedure pro_stu(
@pageIndex int,
@pageSize int
)
as
declare @startRow int, @endRow int
set @startRow = (@pageIndex - 1) * @pageSize +1
set @endRow = @startRow + @pageSize -1
select * from (
select *, row_number() over (order by id asc) as number from student
) t
where t.number between @startRow and @endRow;
exec pro_stu 2, 2;
總結
以上是生活随笔為你收集整理的T-SQL 之 存储过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Laya资源加载小记
- 下一篇: 弱电设计师如何深化设计图纸?附常用的VI