存儲過程

建立存儲過程語法

存儲過程(Stored Procedure)是在大型數據庫系統中,一組爲了完成特定功能的SQL 語句集,存儲在數據庫中,通過第一次編譯後再次調用不須要再次編譯,用戶經過指定存儲過程的名字並給出參數(若是該存儲過程帶有參數)來執行它。存儲過程是數據庫中的一個重要對象。數據庫

CREATE proc | procedure procedure_name
    [{@參數數據類型} [=默認值] [output],
     {@參數數據類型} [=默認值] [output],
     ....
    ]
as
    SQL_statements
go

存儲過程與SQL語句的優劣對比

優點

1.提升性能安全

SQL語句在建立過程時進行分析和編譯。 存儲過程是預編譯的,在首次運行一個存儲過程時,查詢優化器對其進行分析、優化,並給出最終被存在系統表中的存儲計劃,這樣,在執行過程時即可節省此開銷。

2.下降網絡開銷服務器

存儲過程調用時只需用提供存儲過程名和必要的參數信息,從而可下降網絡的流量。

3.便於進行代碼移植網絡

數據庫專業人員能夠隨時對存儲過程進行修改,但對應用程序源代碼卻毫無影響,從而極大的提升了程序的可移植性。

4.更強的安全性函數

1)系統管理員能夠對執行的某一個存儲過程進行權限限制,避免非受權用戶對數據的訪問
2)在經過網絡調用過程時,只有對執行過程的調用是可見的。 所以,惡意用戶沒法看到表和數據庫對象名稱、嵌入本身的 Transact-SQL 語句或搜索關鍵數據
3)使用過程參數有助於避免 SQL 注入攻擊。 由於參數輸入被視做文字值而非可執行代碼,因此,攻擊者將命令插入過程內的 Transact-SQL 語句並損害安全性將更爲困難
4)能夠對過程進行加密,這有助於對源代碼進行模糊處理

劣勢

1.存儲過程須要專門的數據庫開發人員進行維護
2.設計邏輯變動,修改存儲過程沒有SQL靈活性能

N在存儲過程當中的應用

N '字符串'

N後字符串的數據類型爲NChar或是NVarchar優化

使用 N 前綴

在服務器上執行的代碼中(例如在存儲過程和觸發器中)顯示的 Unicode 字符串常量必須以大寫字母 N 爲前綴。即便所引用的列已定義爲 Unicode 類型,也應如此。如不使用 N 前綴,字符串將轉換爲數據庫的默認代碼頁。可能致使不識別某些字符加密

EXECUTE Product_Info @name = N'Chain'

使用 N 前綴的要求適用於在服務器上生成的和客戶端發送的字符串常量設計

存儲過程的具體應用

基礎查詢

建立不帶參數的存儲過程

--查詢存儲過程
IF OBJECT_ID (N'PROC_SELECT_STUDENTS_COUNT', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_STUDENTS_COUNT;
GO
CREATE procedure PROC_SELECT_STUDENTS_COUNT
AS 
    SELECT COUNT(ID) FROM Students
GO
--執行
EXEC PROC_SELECT_STUDENTS_COUNT

帶參數的存儲過程

--查詢存儲過程,根據城市查詢總數
IF OBJECT_ID (N'PROC_SELECT_STUDENTS_BY_CITY_COUNT', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_STUDENTS_BY_CITY_COUNT;
GO
CREATE procedure PROC_SELECT_STUDENTS_BY_CITY_COUNT(@city nvarchar(50))
AS
    SELECT COUNT(ID) FROM Students WHERE City = @city
GO
--執行
EXEC PROC_SELECT_STUDENTS_BY_CITY_COUNT N'Beijing'

帶有通配符

在參數值賦值時,加上相應的通配符code

--查詢姓氏爲李的學生信息,含通配符
IF OBJECT_ID (N'PROC_SELECT_STUDENTS_BY_SURNNAME', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_STUDENTS_BY_SURNNAME;
GO
CREATE procedure PROC_SELECT_STUDENTS_BY_SURNNAME
    @surnName nvarchar(20)='林%' --默認值
AS 
    SELECT ID, Name, Age FROM Students WHERE Name like @surnName
GO
--執行
EXEC PROC_SELECT_STUDENTS_BY_SURNNAME
EXEC PROC_SELECT_STUDENTS_BY_SURNNAME N'林%'
EXEC PROC_SELECT_STUDENTS_BY_SURNNAME N'%林%'

帶有輸出參數

--根據姓名查詢的學生信息,返回學生的城市及年齡
IF OBJECT_ID (N'PROC_SELECT_STUDENTS_BY_NAME', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_STUDENTS_BY_NAME;
GO
CREATE procedure PROC_SELECT_STUDENTS_BY_NAME
    @name nvarchar(50),     --輸入參數
    @city nvarchar(20) out, --輸出參數
    @age  int output        --輸入輸出參數
AS 
    SELECT @city = City, @age = Age FROM Students WHERE Name = @name AND Age = @age
GO
--執行
declare @name nvarchar(50),
        @city nvarchar(20),
        @age int;
set @name = N'PG ONE';
set @age = 23;
exec PROC_SELECT_STUDENTS_BY_NAME @name, @city out, @age output;
select @city, @age;

使用存儲過程進行增刪改

新增

--存儲過程:新增學生信息
IF OBJECT_ID (N'PROC_INSERT_STUDENT', N'P') IS NOT NULL
    DROP procedure PROC_INSERT_STUDENT;
GO
CREATE procedure PROC_INSERT_STUDENT
    @id int,
    @name nvarchar(20),
    @age int,
    @city nvarchar(20)
AS 
    INSERT INTO Students(ID,Name,Age,City) VALUES(@id,@name,@age,@city)
GO
--執行
EXEC PROC_INSERT_STUDENT 1001, N'GAI', 29, 'ChengDu'

修改

--根據學生ID,更新學生信息
IF OBJECT_ID (N'PROC_UPDATE_STUDENT', N'P') IS NOT NULL
    DROP procedure PROC_UPDATE_STUDENT;
GO
CREATE procedure PROC_UPDATE_STUDENT
    @id int,
    @name nvarchar(20),
    @age int,
    @city nvarchar(20)
AS 
    UPDATE Students SET Name=@name, Age=@age, City=@city WHERE ID=@id
GO

--執行
EXEC PROC_UPDATE_STUDENT 1001, N'Jony J', 28, 'NanChang'

刪除

--存儲過程:刪除學生信息
IF OBJECT_ID (N'PROC_DELETE_STUDENT_BY_ID', N'P') IS NOT NULL
    DROP procedure PROC_DELETE_STUDENT_BY_ID;
GO
CREATE procedure PROC_DELETE_STUDENT_BY_ID
    @id int
AS 
    DELETE FROM  Students WHERE ID=@id
GO
--執行
EXEC PROC_DELETE_STUDENT_BY_ID 1001

存儲過程實現分頁查詢

使用row_number函數分頁

--分頁查詢
IF OBJECT_ID (N'PROC_SELECT_BY_PAGE', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_BY_PAGE;
GO
CREATE procedure PROC_SELECT_BY_PAGE
    @startIndex int,
    @endIndex int
AS 
    SELECT  * FROM (SELECT ID,Name,Age,City,ROW_NUMBER() OVER(ORDER BY ID DESC) AS RowNumber FROM Students) AS Temp 
    WHERE Temp.RowNumber BETWEEN @startIndex AND @endIndex
GO
--執行
EXEC PROC_SELECT_BY_PAGE 1,10

使用傳統的top分頁

--使用TOP分頁
IF OBJECT_ID (N'PROC_SELECT_BY_PAGE_WITH_TOP', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_BY_PAGE_WITH_TOP;
GO
CREATE procedure PROC_SELECT_BY_PAGE_WITH_TOP
    @pageIndex int,
    @pageSize int
AS 
    SELECT TOP(@pageSize) * FROM Students 
    WHERE ID >=(SELECT MAX(ID) FROM (SELECT TOP(@pageSize*(@pageIndex-1) + 1) ID FROM Students ORDER BY ID) AS Temp)    
GO
--執行
EXEC PROC_SELECT_BY_PAGE_WITH_TOP 1,2

其餘功能

存儲過程,每次執行都進行從新編譯

--存儲過程,重複編譯
IF OBJECT_ID (N'PROC_SELECT_STUDENTS_WITH_RECOMPILE', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_STUDENTS_WITH_RECOMPILE;
GO
CREATE procedure PROC_SELECT_STUDENTS_WITH_RECOMPILE
with recompile --重複編譯
AS 
    SELECT * FROM Students
GO

對存儲過程進行加密

加密後,不能查看和修改源腳本

--查詢存儲過程,進行加密
IF OBJECT_ID (N'PROC_SELECT_STUDENTS_WITH_ENCRYPTION', N'P') IS NOT NULL
    DROP procedure PROC_SELECT_STUDENTS_WITH_ENCRYPTION;
GO
CREATE procedure PROC_SELECT_STUDENTS_WITH_ENCRYPTION
with encryption --加密
AS 
    SELECT * FROM Students
GO
--執行
EXEC PROC_SELECT_STUDENTS_WITH_ENCRYPTION