一、exec和sp_executesql介紹
當需要根據外部輸入的參數來決定要執行的SQL語句時,常常需要動態來構造SQL查詢語句。比如,一個比較通用的分頁存儲過程,可能需要傳入表名,字段,過濾條件,排序等參數,而對於搜索的話,可能要根據搜索條件判斷來動態執行SQL語句。
在SQL Server中有兩種方式來執行動態SQL語句,分別是exec和sp_executesql。sp_executesql相對而言具有更多的優點,它提供了輸入輸出接口,可以將輸入輸出變量直接傳遞到SQL語句中,而exec只能通過拼接的方式來實現。還有一個優點就是sp_executesql,能夠重用執行計划,這就大大提高了執行的性能。所以一般情況下建議選擇sp_executesql來執行動態SQL語句。
使用sp_executesql需要注意的一點就是,它后面執行的SQL語句必須是Unicode編碼的字符串,所以在聲明存儲動態SQL語句的變量時必須聲明為nvarchar類型,否則在執行的時候會報“過程需要類型為 'ntext/nchar/nvarchar' 的參數 '@statement'”的錯誤,如果是使用sp_executesql直接執行SQL語句,則必須在前面加上大寫字母N,以表明后面的字符串是使用Unicode類型編碼的。
在SQL中,字符串前加N,表示雙字節字符,即字符串用Unicode方式存儲。對於西文字符,用一個字節來存儲過足夠了,對於東方文字字符,就需要兩個字節來存儲。Unicode 為了統一、規范、方便、兼容,就規定西文字符也用兩個字節來存儲。
下面看幾種不同的動態執行SQL的例子
--1,普通SQL
exec ('select GETDATE();');
exec sp_executesql N'select GETDATE();';--加上N,不用Unicode表示方式會報錯
--2,帶參數的SQL語句
declare @tempsql nvarchar(1024);
declare @start_date datetime;
set @start_date = '2018-12-24';
set @tempsql = 'select DATEDIFF(day, @start_date,GETDATE());';
exec('select DATEDIFF(day, ''' + @start_date + ''',GETDATE());')
exec sp_executesql @tempsql, N'@start_date datetime',@start_date;
--3,有返回值的SQL語句
declare @tempsql nvarchar(1024);
declare @temp datetime;
set @tempsql = N'select @temp = GETDATE();';
exec sp_executesql @tempsql,N'@temp datetime output',@temp output;
select @temp
執行普通SQL時,exec和sp_executesql沒有太大差別。
執行帶參數的sql時,exec只能以字符串拼接的方式執行,sp_executesql可以以傳參的方式執行,更安全一些
執行有返回值的sql時,只能使用sp_executesql
二、存儲過程中動態執行SQL,並且獲取執行的值
假如在存儲過程中需要動態執行SQL,並且還要把動態執行的結果,比如行數、sum的總數等,賦值給變量,或者賦值到臨時表里,那么就可以用exec和sp_executesql結合使用,如下例子
IF object_id('tempdb..#tempTB') IS NOT NULL
BEGIN
DROP TABLE #tempTB
END
--創建臨時表
CREATE TABLE #tempTB(
testTime datetime NULL,
testTime2 datetime NULL,
)
--將查詢的數據insert到臨時表
declare @tempSQL nvarchar(1000);
set @tempSQL = 'insert into #tempTB (testTime,testTime2) select GETDATE(), DATEADD(day, 1,GETDATE());';
exec(@tempSQL);
select * from #tempTB;
--動態查詢臨時表行數,並賦值到變量
declare @tempCon int;
set @tempSQL = N'select @tempCon = count(*) from #tempTB;';
exec sp_executesql @tempSQL,N'@tempCon int output',@tempCon output;
select @tempCon;
DROP TABLE #tempTB