存儲過程
存儲過程(Stored Procedure)簡稱過程,由一系列SQL語句構成完成一定的功能的程序段,通過過程名調用並執行。在 SqlServer 中存放在數據庫的“可編程性”組件中,與表和視圖的級別相同。存儲過程是由流程控制和 SQL 語句組成的,允許用戶聲明變量,可以調用系統函數,經編譯后存儲在數據庫服務器中。存儲過程可以接收輸入參數,也可以將運行結果帶出過程,返回執行存儲過程的狀態值,還可以嵌套調用。由於存儲過程在服務器運行,且運行后會保存在緩存中,因此可以提高運行效率。
存儲過程分為三類,分別是系統存儲過程、用戶自定義存儲過程和擴展存儲過程。
| 存儲過程類型 | 說明 |
|---|---|
| 系統存儲過程 | 系統自動創建的系統存儲過程,用於顯示各種參數等,以 “sp_” 為前綴 |
| 自定義存儲過程 | 由用戶為完成某一特定功能而編寫的存儲過程,名稱不能以 “sp_” 為前綴 |
| 擴展存儲過程 | 是對動態鏈接庫(DLL)函數的調用,前綴名是 “XP_” |
創建存儲過程的步驟是:
- 檢驗存儲過程名是否存在;
- 編寫存儲過程中的 SQL 語句;
- 用創建存儲過程的語法創建存儲過程。
SQL 語句
建立存儲過程前需要確定其功能,存儲過程的調用可以是無參數調用或有參數調用,可以有返回值或者沒有參數返回。建立存儲過程的命令格式是:
CREATE PROCEDURE 存儲過程名
[WITH ENCRYPTION]
[@參數名 類型 [ = 默認值][OUTPUT]][,-n ]
AS SQL語句
| 參數 | 說明 |
|---|---|
| PROCEDURE | 存儲過程名 |
| WITH ENCRYPTION | 將存儲過程的代碼加密 |
| @參數名 類型 | 接收指定的實際參數及類型 |
| OUTPUT | 表示參數是輸出參數,若無此項則參數是輸入參數 |
| SQL 語句 | 是構造存儲過程的 SQL 語句,如果包括多條命令可以用 BEGIN…END 代碼塊 |
調用存儲過程的方式有以下 2 種:
存儲過程名 [參數值] --存儲過程名是批處理的第 1 條語句
EXECUTE 存儲過程名 [參數值] --存儲過程名不是批處理的第 1 條語句
存儲過程樣例
樣例一
此時有成績表 Score 和課程表 Course,表中具有以下字段和記錄。


用戶輸入某一門課程的名稱,就可統計出該課程各分數段分布的人數。首先建立排名表:
CREATE TABLE Rank(
division char(20),
sub_sum int
);
INSERT INTO Rank(division) VALUES('[0,60)'),('[60,70)'),('[70,80)'),('[80,90)'),('[90,100]');
接着創建存儲過程。
CREATE PROCEDURE printcourse @pcname char(20)
AS
DECLARE @pcno char(20), @pcount int, @count int
set nocount on
SELECT @count = COUNT(*) FROM Course WHERE Cname = @pcname
IF(@count = 0)
BEGIN
RAISERROR('您輸入的課程不存在,請重新輸入!', 16, 1)
RETURN
END
--查找 @pcname 對應的課程號
SELECT @pcno = Score.cno FROM Course, Score WHERE Course.Cno = Score.Cno AND Course.Cname = @pcname
--統計不及格人數,並更新 Rank 表
SELECT @pcount = COUNT(*) FROM Score WHERE Degree < 60 AND Cno = @pcno
UPDATE Rank SET sub_sum = @pcount WHERE division = '[0,60)'
SELECT @pcount = COUNT(*) FROM Score WHERE Degree >= 60 AND Degree < 70 AND Cno = @pcno
UPDATE Rank SET sub_sum = @pcount WHERE division = '[60,70)'
SELECT @pcount = COUNT(*) FROM Score WHERE Degree >= 70 AND Degree < 80 AND Cno = @pcno
UPDATE Rank SET sub_sum = @pcount WHERE division='[70,80)'
SELECT @pcount = COUNT(*) FROM Score WHERE Degree >= 80 AND Degree < 90 AND Cno = @pcno
UPDATE Rank SET sub_sum = @pcount WHERE division='[80,90)'
SELECT @pcount = COUNT(*) FROM Score WHERE Degree >= 90 AND Degree <= 100 AND Cno = @pcno
UPDATE Rank SET sub_sum = @pcount WHERE division = '[90,100]'
最后嘗試執行該存儲過程。
EXEC printcourse '計算機導論'
SELECT * FROM Rank

樣例二
統計某一門課的平均成績,存儲過程可帶有一個字符型參數值,接受用戶輸入的課程名稱。一個輸出參數(用 output 聲明)用於存放返回給調用者的這門課程的平均成績)。
CREATE PROCEDURE printavg_course @pcname char(20), @pavg int output
AS
DECLARE @pcno char(20)
SET NOCOUNT ON
SELECT @count = COUNT(*) FROM Course WHERE Cname = @pcname
IF(@count = 0)
BEGIN
RAISERROR('您輸入的課程不存在,請重新輸入!', 16, 1)
RETURN
END
--最關鍵的 2 行
SELECT @pcno = Score.cno FROM Course, Score WHERE Course.Cno = Score.Cno AND Course.Cname = @pcname
SELECT @pavg = AVG(Score.Degree) FROM Score WHERE Cno = @pcno
PRINT RTrim(@pcname) + '的平均成績為:' + CAST(@pavg AS char(5))
運行該存儲過程。
DECLARE @pavg int
EXEC printavg_course '操作系統', @pavg output
Select @pavg int

樣例三
在 Course 表中查詢學生的學號、課程號和成績,但是要將學生選課成績從百分制改為等級制,即 A、B、C、D、E 五級。
CREATE PROCEDURE printclass_degree
AS
SELECT Sno, Cno, Degree,
CASE
WHEN Degree < 60 THEN '不及格'
WHEN Degree >= 60 AND Degree < 70 THEN '及格'
WHEN Degree >= 70 AND Degree < 80 THEN '中'
WHEN Degree >= 80 AND Degree < 90 THEN '良'
WHEN Degree >= 90 AND Degree <= 100 THEN '優'
ELSE '成績為空!'
END AS 'Degree Classified'
FROM Score
運行該存儲過程。
EXEC printclass_degree

樣例四
表 Course 增加兩列,分別是課程選修最大人數(mn ,默認50)和當前人數(cn ,默認0)。

某學生沒有選過某課程,要選某課程且沒有超過課程最大人數時,可以選擇該課程,當前選課人數加1,並在成績 Score 表中增加對應的該生該課程的成績為 0 記錄。否則,提示該課程選課人數已滿,不能選課。
CREATE PROCEDURE cs @sno char(3), @cno char(5)
AS
DECLARE @mn INT, @cn INT, @count INT
SET @count = (SELECT count(Sno) FROM Score WHERE Sno = @sno AND Cno = @cno)
IF(@count = 1)
BEGIN
RAISERROR('該學生已經選過這門課',16,1)
END
ELSE
BEGIN
SELECT @mn = mn FROM Course WHERE Cno = @Cno
SET @count = (SELECT count(Sno) FROM Score WHERE Cno = @Cno)
IF(@count >= @mn)
BEGIN
RAISERROR('該課程已達到最大選課人數',16,1)
END
ELSE
BEGIN
UPDATE Course SET cn = @count + 1 WHERE Cno = @cno
INSERT INTO Score(Sno, Cno, Degree) VALUES(@sno, @cno, 0)
RAISERROR('學生選課成功',16,1)
END
END
運行該存儲過程。
EXEC cs '101', '3-245'
SELECT * FROM Score
SELECT * FROM Course

EXEC cs '101', '3-235'

某學生選了某課程后要退選,該課程的當前選課人數減 1 並刪除其對應的成績。
CREATE PROCEDURE csd @sno char(3), @cno char(5)
AS
DECLARE @mn INT, @cn INT, @count INT
SET @count = (SELECT count(Sno) FROM Score WHERE Sno = @sno AND Cno = @cno)
IF(@count = 0)
BEGIN
RAISERROR('該學生沒有選過這門課',16,1)
END
ELSE
BEGIN
SELECT @cn = cn FROM Course010 WHERE Cno = @Cno
UPDATE Course SET cn = @cn - 1 WHERE Cno = @cno
DELETE Score WHERE Sno = @sno AND Cno = @cno
RAISERROR('學生退課成功',16,1)
END
運行該存儲過程。
EXEC csd '101', '3-245'
SELECT * FROM Score
SELECT * FROM Course

EXEC csd '101', '3-245'

某學生選了某課程后,可以查詢其成績(out)。
CREATE PROCEDURE cx @sno char(3), @cno char(5)
AS
DECLARE @mn INT, @cn INT, @count INT
SET @count = (SELECT count(Sno) FROM Score WHERE Sno = @sno AND Cno = @cno)
IF(@count = 0)
BEGIN
RAISERROR('該學生沒有選過這門課',16,1)
END
ELSE
BEGIN
SELECT * FROM Score WHERE Cno = @cno AND Sno = @sno
END
運行該存儲過程。
EXEC cx '101', '3-245'

如果學生沒有選課,則看到如下信息。

參考資料
《SqlServer 2014 數據庫技術實用教程》,胡伏湘、肖玉朝 主編,清華大學出版社
