SQL Server 中 EXEC 與 SP_EXECUTESQL 的區別

轉載自:https://www.cnblogs.com/lxblog/archive/2013/01/14/2859828.htmlhtml

 

MSSQL爲咱們提供了兩種動態執行SQL語句的命令,分別是 EXEC 和 SP_EXECUTESQL ,咱們先來看一下兩種方式的用法。sql

先創建一個表,並添加一些數據來進行演示:數據庫

複製代碼
CREATE TABLE t_student(
 Id INT NOT NULL,
 Name NVARCHAR (10) NULL,
 Age TINYINT NULL,
 School NVARCHAR(20) NULL,
 Class NVARCHAR(10) NULL,
 Score FLOAT NULL,
 CONSTRAINT [PK_Student_Id] PRIMARY KEY CLUSTERED(Id)
)
GO

INSERT INTO t_student VALUES(1,'張小紅',8,'育才小學','一班',92)
INSERT INTO t_student VALUES(2,'王麗麗',8,'育才小學','一班',90)
INSERT INTO t_student VALUES(3,'張燕',7,'雲華小學','二班',86)
INSERT INTO t_student VALUES(4,'劉華',6,'雲華小學','二班',85)
複製代碼

1、EXEC 安全

EXEC命令能夠執行一個存儲過程也能夠執行一個動態SQL語句。先來看看怎麼執行存儲過程:服務器

新建一個存儲過程 SP_GetStudent ,返回 成績大於90 分的學生:函數

複製代碼
CREATE PROCEDURE [dbo].[Sp_GetStudent]
    @Score FLOAT,
    @Nums INT OUTPUT 
AS
BEGIN
    SET NOCOUNT ON;
    SELECT * FROM t_student WHERE Score >=@Score
    SELECT @Nums=COUNT(1) FROM t_student WHERE Score >=@Score
    IF(@Nums>0)
     RETURN 1
    ELSE
     RETURN 0
END
GO
複製代碼

 該存儲過程涉及了 查詢操做、返回值和輸出參數,咱們來看用EXEC 命令如何調用:性能

複製代碼
DECLARE @return_value int,
        @OutNums int

EXEC    @return_value = [dbo].[Sp_GetStudent] 
        @Score = 90,
        @Nums = @OutNums OUTPUT

SELECT  @OutNums as N'大於90分的人數'

SELECT  '返回值' = @return_value
GO
複製代碼

 執行結果:優化

咱們發現EXEC 執行存儲過程和咱們平時程序執行一個方法是幾乎同樣的,返回值參數 直接就能夠等於存儲過程的執行後的返回值,輸出參數 在後面須要增長 OUTPUT 關鍵字。spa

執行存儲過程不是重點,重點是執行動態sql語句,一樣看一下例子code

DECLARE @TableName NVARCHAR(50),@Sql NVARCHAR(MAX),@Score INT;
SET @TableName = 't_Student';
SET @Score = 90;
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) +'WHERE Score >= '+CAST(@Score AS NVARCHAR(10))
EXEC (@sql);

 執行結果:

注意:在執行拼接SQL 語句的時候,的EXEC括號中只容許包含一個字符串變量,可是能夠串聯多個變量,若是咱們直接執行這個SQL語句:

--這是錯誤的調用
EXEC ('SELECT * FROM '+QUOTENAME(@TableName) +'WHERE Score >= '+CAST(@Score AS NVARCHAR(10)));

 執行就會提示錯誤。可是這樣就沒有問題:

複製代碼
DECLARE @TableName NVARCHAR(50),@Sql NVARCHAR(MAX),@Score INT
DECLARE @Sql2 NVARCHAR(MAX)
SET @TableName = 't_Student';
SET @Score = 90;
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName)
SET @Sql2=' WHERE Score >= '+CAST(@Score AS NVARCHAR(10))
EXEC (@sql+@sql2)
複製代碼

 EXEC 執行拼接sql語句的時候不支持 嵌入式參數,以下:

DECLARE @OUT_Nums INT,@IN_Score INT,@Sql NVARCHAR(MAX)
SET @IN_Score = 90
SET @sql = 'SELECT @Nums=COUNT(1) FROM t_student WHERE Score >= @Score'
EXEC (@sql)

 

經過上面的代碼發現,EXEC 執行拼接的SQL語句的時候,不支持內嵌參數,包括輸入參數和輸出參數。有的時候咱們想把獲得的count(*)傳出來,用EXEC是很差辦到的。接下來,再來看看SP_EXECUTESQL的使用:

2、SP_EXECUTESQL:

SP_EXECUTESQL 是在 SQL 2005中引入的新的系統存儲過程,也是用來處理動態SQL 語句的。它比EXEC 更加靈活,首先也執行一下第一次的拼接SQL語句:

DECLARE @TableName NVARCHAR(50),@Sql NVARCHAR(MAX),@Score INT;
SET @TableName = 't_Student';
SET @Score = 90;
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) +'WHERE Score >= '+CAST(@Score AS NVARCHAR(10))
EXEC SP_EXECUTESQL @sql --注意這裏沒有了()

執行結果:

SP_EXECUTESQL 支持內嵌參數:

先來看一下SP_EXECUTESQL的語法:

sp_executesql [ @stmt = ] stmt
[ 
    {, [@params=] N'@parameter_name data_type [ OUT | OUTPUT ][,...n]' } 
     {, [ @param1 = ] 'value1' [ ,...n ] }
]

說明:
[ @stmt = ] stmt 包含 Transact-SQL 語句或批處理的 Unicode 字符串。stmt 必須是 Unicode 常量或 Unicode 變量。不容許使用更復雜的 Unicode 表達式(例如使用 + 運算符鏈接兩個字符串)。不容許使用字符常量。若是指定了 Unicode 常量,則必須使用 N 做爲前綴。例如,Unicode 常量 N'sp_who' 是有效的,可是字符常量 'sp_who' 則無效。字符串的大小僅受可用數據庫服務器內存限制。在 64 位服務器中,字符串大小限制爲 2 GB,即 nvarchar(max) 的最大大小。stmt 中包含的每一個參數在 @params 參數定義列表和參數值列表中均必須有對應項

[ @params = ] N'@parameter_namedata_type[ ,... n ] ' 包含 stmt 中嵌入的全部參數定義的字符串。字符串必須是 Unicode 常量或 Unicode 變量。每一個參數定義由參數名稱和數據類型組成。n 是表示附加參數定義的佔位符。在 stmt 中指定的每一個參數必須在 @params 中定義。若是 stmt 中的 Transact-SQL 語句或批處理不包含參數,則不須要 @params。該參數的默認值爲 NULL。

[ @param1 = ] 'value1'

參數字符串中定義的第一個參數的值。該值能夠是 Unicode 常量,也能夠是 Unicode 變量。必須爲 stmt 中包含的每一個參數提供參數值。若是 stmt 中的 Transact-SQL 語句或批處理沒有參數,則不須要這些值。

 

 

[ OUT | OUTPUT ]

指示參數是輸出參數。除非是公共語言運行 (CLR) 過程,不然 textntext 和 image 參數都可用做 OUTPUT 參數。使用 OUTPUT 關鍵字的輸出參數能夠爲遊標佔位符,CLR 過程除外。

 

 

 

 

n 附加參數值的佔位符。這些值只能爲常量或變量,不能是很複雜的表達式(例如函數)或使用運算符生成的表達式。

返回代碼值 : 

0(成功)或非零(失敗)

 

 

 

 

 

 

 

結果集:從生成 SQL 字符串的全部 SQL 語句返回結果集

 

 

看不懂沒有關係,經過例子就會很是明白的,依舊還執行上面的 SQL 語句:

DECLARE @OUT_Nums INT,@IN_Score INT,@Sql NVARCHAR(MAX)
SET @IN_Score = 90
SET @sql = 'SELECT @Nums=COUNT(1) FROM t_student WHERE Score >= @Score'
EXEC SP_EXECUTESQL @sql,N'@Nums INT OUT,@Score INT',@OUT_Nums OUTPUT,@IN_Score
SELECT @OUT_Nums AS '人數'

 執行結果:

須要注意的是:

一、要求動態Sql和動態Sql參數列表必須是NVARCHAR

二、動態Sql的參數列表與外部提供值的參數列表順序必需一致

三、一旦使用了 '@name = value' 形式以後,全部後續的參數就必須以 '@name = value' 的形式傳遞,好比:

DECLARE @OUT_Nums INT,@IN_Score INT,@Sql NVARCHAR(MAX)
SET @IN_Score = 90
SET @sql = 'SELECT @Nums=COUNT(1) FROM t_student WHERE Score >= @Score'
EXEC SP_EXECUTESQL @stmt=@sql,@params=N'@Nums INT OUT,@Score INT',@Nums=@OUT_Nums OUTPUT,@Score=@IN_Score
SELECT @OUT_Nums AS '人數'

經過上面的例子已經很清晰的代表了,在執行動態SQL 語句的時候,EXEC 和  SP_EXECUTESQL 的區別了,來總結一下:

一、 性能:

官方描述:sp_executesql stmt 參數中的 Transact-SQL 語句或批處理在執行 sp_executesql 語句時才編譯。隨後,將編譯 stmt 中的內容,並將其做爲執行計劃運行。該執行計劃獨立於名爲 sp_executesql 的批處理的執行計劃。sp_executesql 批處理不能引用調用 sp_executesql 的批處理中聲明的變量。sp_executesql 批處理中的本地遊標或變量對調用 sp_executesql 的批處理是不可見的。對數據庫上下文所作的更改只在 sp_executesql 語句結束前有效。若是隻更改了語句中的參數值,則sp_executesql 可用來代替存儲過程屢次執行 Transact-SQL 語句。由於 Transact-SQL 語句自己保持不變,僅參數值發生變化,因此 SQL Server 查詢優化器可能重複使用首次執行時所生成的執行計劃。

說通俗一點就是:若是用 EXEC 執行一條動態 SQL 語句,因爲每次傳入的參數不同,因此每次生成的 @sql 就不同,這樣每執行一次SQL SERVER 就必須從新將要執行的動態 Sql 從新編譯一次 。可是SP_EXECUTESQL 則不同,因爲將數值參數化,要執行的動態 Sql 永遠不會變化,只是傳入的參數的值在變化,那每次執行的時候就不用從新編譯,速度和效率天然有所提高。

二、從上面的例子咱們已經可以看出 SP_EXECUTESQL 命令比 EXEC 命令更靈活,由於它提供一個接口,該接口及支持輸入參數也支持輸出參數。

三、EXEC 執行純動態SQL,執行時可能沒法使用預編譯的執行計劃,關鍵是不安全,能夠致使 SQL 注入 ,而 SP_EXECUTESQL 執行參數化動態 SQL ,執行時能使用預編譯的執行計劃,並且保存存儲過程時就能夠肯定可使用的預編譯的執行計劃,並且最重要的是「安全」,自然免疫SQL 注入

相關文章
相關標籤/搜索