SQL Server存儲過程詳解

轉載:https://blog.csdn.net/younghaiqing/article/details/62884658html

 

一. 什麼是存儲過程

系統存儲過程是系統建立的存儲過程,目的在於可以方便的從系統表中查詢信息或完成與更新數據庫表相關的管理任務或其餘的系統管理任務。系統存儲過程主要存儲在master數據庫中,以「sp」下劃線開頭的存儲過程。儘管這些系統存儲過程在master數據庫中,但咱們在其餘數據庫仍是能夠調用系統存儲過程。有一些系統存儲過程會在建立新的數據庫的時候被自動建立在當前數據庫中。

二. 存儲過程運行流程

這裏寫圖片描述

存儲過程是由一些SQL語句和控制語句組成的被封裝起來的過程,它駐留在數據庫中,能夠被客戶應用程序調用,也能夠從另外一個過程或觸發器調用。它的參數能夠被傳遞和返回。與應用程序中的函數過程相似,存儲過程能夠經過名字來調用,並且它們一樣有輸入參數和輸出參數。

根據返回值類型的不一樣,咱們能夠將存儲過程分爲三類:

  • 返回記錄集的存儲過程的執行結果是一個記錄集,典型的例子是從數據庫中檢索出符合某一個或幾個條件的記錄;
  • 返回數值的存儲過程執行完之後返回一個值,例如在數據庫中執行一個有返回值的函數或命令;
  • 行爲存儲過程僅僅是用來實現數據庫的某個功能,而沒有返回值,例如在數據庫中的更新和刪除操做。

我的認爲,存儲過程說白了就是一堆 SQL 的合併。中間加了點邏輯控制。

  1. 可是存儲過程處理比較複雜的業務時比較實用。好比說,

一個複雜的數據操做。若是你在前臺處理的話。可能會涉及到屢次數據庫鏈接。但若是你用存儲過程的話。就只有一次。從響應時間上來講有優點。

  1. 也就是說存儲過程能夠給咱們帶來運行效率提升的好處。

另外,程序容易出現 BUG 不穩定,而存儲過程,只要數據庫不出現問題,基本上是不會出現什麼問題的。也就是說從安全上講,使用了存儲過程的系統更加穩定。

那麼問題來了,何時才能夠用存儲?對於數據量不是很大以及業務處理不是很複雜的小項目就無須要了麼?

答:錯。存儲過程不只僅適用於大型項目,對於中小型項目,使用存儲過程也是很是有必要的。其威力和優點主要體如今:

  1. 存儲過程只在創造時進行編譯,之後每次執行存儲過程都不需再從新編譯,而通常 SQL 語句每執行一次就編譯一次,因此使用存儲過程可提升數據庫執行速度。
  2. 當對數據庫進行復雜操做時(如對多個表進行 Update,Insert,Query,Delete 時),可將此複雜操做用存儲過程封裝起來與數據庫提供的事務處理結合一塊兒使用。這些操做,若是用程序來完成,就變成了一條條的 SQL 語句,可能要屢次鏈接數據庫。而換成存儲,只須要鏈接一次數據庫就能夠了。
  3. 存儲過程能夠重複使用,可減小數據庫開發人員的工做量。
  4. 安全性高,可設定只有某此用戶才具備對指定存儲過程的使用權。
  5. 減小網絡通訊量。調用一個行數很少的存儲過程與直接調用SQL語句的網絡通訊量可能不會有很大的差異,但是若是存儲過程包含上百行SQL語句,那麼其性能絕對比一條一條的調用SQL語句要高得多。
  6. 執行速度更快。有兩個緣由:首先,在存儲過程建立的時候,數據庫已經對其進行了一次解析和優化。其次,存儲過程一旦執行,在內存中就會保留一份這個存儲過程,這樣下次再執行一樣的存儲過程時,能夠從內存中直接調用。
  7. 更強的適應性:因爲存儲過程對數據庫的訪問是經過存儲過程來進行的,所以數據庫開發人員能夠在不改動存儲過程接口的狀況下對數據庫進行任何改動,而這些改動不會對應用程序形成影響。
  8. 布式工做:應用程序和數據庫的編碼工做能夠分別獨立進行,而不會相互壓制。

存儲過程的使用,好像一直是一個爭論。

我不傾向於儘量使用存儲過程,是這麼認爲的:

  1. 運行速度: 大多數高級的數據庫系統都有statement cache的,因此編譯sql的花費沒什麼影響。可是執行存儲過程要比直接執行sql花費更多(檢查權限等),因此對於很簡單的sql,存儲過程沒有什麼優點。
  2. 網絡負荷:若是在存儲過程當中沒有屢次數據交互,那麼實際上網絡傳輸量和直接sql是同樣的。
  3. 團隊開發:很遺憾,比起成熟的IDE,沒有什麼很好存儲過程的IDE工具來支持,也就是說,這些必須手工完成。
  4. 安全機制:對於傳統的C/S結構,鏈接數據庫的用戶能夠不一樣,因此安全機制有用;可是在web的三層架構中,數據庫用戶不是給用戶用的,因此基本上,只有一個用戶,擁有全部權限(最多還有一個開發用戶)。這個時候,安全機制有點多餘。
  5. 用戶滿意:實際上這個只是要將訪問數據庫的接口統一,是用存儲過程,仍是EJB,沒太大關係,也就是說,在三層結構中,單獨設計出一個數據訪問層,一樣能實現這個目標。
  6. 開發調試:同樣因爲IDE的問題,存儲過程的開發調試要比通常程序困難(老版本DB2還只能用C寫存儲過程,更是一個災難)。
  7. 移植性:算了,這個不用提,反正通常的應用老是綁定某個數據庫的,否則就沒法靠優化數據庫訪問來提升性能了。
  8. 維護性:的確,存儲過程有些時候比程序容易維護,這是由於能夠實時更新DB端的存儲過程,可是在3層結構下,更新server端的數據訪問層同樣能實現這個目標,惋惜如今不少平臺不支持實時更新而已。

經常使用系統存儲過程有:

exec sp_databases; --查看數據庫 exec sp_tables; --查看錶 exec sp_columns student;--查看列 exec sp_helpIndex student;--查看索引 exec sp_helpConstraint student;--約束 exec sp_stored_procedures; exec sp_helptext 'sp_stored_procedures';--查看存儲過程建立、定義語句 exec sp_rename student, stuInfo;--修改表、索引、列的名稱 exec sp_renamedb myTempDB, myDB;--更改數據庫名稱 exec sp_defaultdb 'master', 'myDB';--更改登陸名的默認數據庫 exec sp_helpdb;--數據庫幫助,查詢數據庫信息 exec sp_helpdb master;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

系統存儲過程示例:

--表重命名
exec sp_rename 'stu', 'stud'; select * from stud; --列重命名 exec sp_rename 'stud.name', 'sName', 'column'; exec sp_help 'stud'; --重命名索引 exec sp_rename N'student.idx_cid', N'idx_cidd', N'index'; exec sp_help 'student'; --查詢全部存儲過程 select * from sys.objects where type = 'P'; select * from sys.objects where type_desc like '%pro%' and name like 'sp%';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

用戶自定義存儲過程

create proc | procedure pro_name [{@參數數據類型} [=默認值] [output], {@參數數據類型} [=默認值] [output], .... ] as SQL_statements
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

二、 建立不帶參數存儲過程

--建立存儲過程 if (exists (select * from sys.objects where name = 'proc_get_student')) drop proc proc_get_student go create proc proc_get_student as select * from student; --調用、執行存儲過程 exec proc_get_student;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

三、 修改存儲過程

--修改存儲過程 alter proc proc_get_student as select * from student;
  • 1
  • 2
  • 3
  • 4

四、 帶參存儲過程

--帶參存儲過程 if (object_id('proc_find_stu', 'P') is not null) drop proc proc_find_stu go create proc proc_find_stu(@startId int, @endId int) as select * from student where id between @startId and @endId go exec proc_find_stu 2, 4;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

五、 帶通配符參數存儲過程

--帶通配符參數存儲過程 if (object_id('proc_findStudentByName', 'P') is not null) drop proc proc_findStudentByName go create proc proc_findStudentByName(@name varchar(20) = '%j%', @nextName varchar(20) = '%') as select * from student where name like @name and name like @nextName; go exec proc_findStudentByName; exec proc_findStudentByName '%o%', 't%';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

六、 帶輸出參數存儲過程

if (object_id('proc_getStudentRecord', 'P') is not null) drop proc proc_getStudentRecord go create proc proc_getStudentRecord( @id int, --默認輸入參數 @name varchar(20) out, --輸出參數 @age varchar(20) output--輸入輸出參數 ) as select @name = name, @age = age from student where id = @id and sex = @age; go -- declare @id int, @name varchar(20), @temp varchar(20); set @id = 7; set @temp = 1; exec proc_getStudentRecord @id, @name out, @temp output; select @name, @temp; print @name + '#' + @temp; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

七、 不緩存存儲過程

--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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

八、 加密存儲過程

--加密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';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

九、 帶遊標參數存儲過程

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;--刪除遊標
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

十、 分頁存儲過程

---存儲過程、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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Raiserror返回用戶定義的錯誤信息,能夠指定嚴重級別,設置系統變量記錄所發生的錯誤。

Raiserror({msg_id | msg_str | @local_variable} {, severity, state} [,argument[,…n]] [with option[,…n]] )
  • 1
  • 2
  • 3
  • 4
  • 5

# msg_id:在sysmessages系統表中指定的用戶定義錯誤信息python

# msg_str:用戶定義的信息,信息最大長度在2047個字符。web

# severity:用戶定義與該消息關聯的嚴重級別。當使用msg_id引起使用sp_addmessage建立的用戶定義消息時,raiserror上指定嚴重性將覆蓋sp_addmessage中定義的嚴重性。sql

任何用戶能夠指定0-18直接的嚴重級別。只有sysadmin固定服務器角色經常使用或具備alter trace權限的用戶才能指定19-25直接的嚴重級別。19-25之間的安全級別須要使用with log選項。
  • 1
  • 2

# state:介於1至127直接的任何整數。State默認值是1。數據庫

raiserror('is error', 16, 1);
select * from sys.messages; --使用sysmessages中定義的消息 raiserror(33003, 16, 1); raiserror(33006, 16, 1);
  • 1
  • 2
  • 3
  • 4
  • 5

http://www.cnblogs.com/hoojo/archive/2011/07/19/2110862.html 
http://www.cnblogs.com/teroy/archive/2013/05/09/3045236.html 
http://www.cnblogs.com/selene/p/4483612.html 
http://www.cnblogs.com/xiaozhuoyun/archive/2008/06/11/1217415.html 
http://www.cnblogs.com/ashleyboy/p/3731550.html 
http://www.cnblogs.com/kerrycode/archive/2010/08/14/1799392.html 
http://www.cnblogs.com/shpchan/archive/2009/09/23/1572915.html 
http://blog.csdn.net/smeyou/article/details/7580862 
http://www.jianshu.com/p/d0ec20874f40 
http://www.cnblogs.com/zhanht/p/5424469.html緩存

相關文章
相關標籤/搜索