SQl 動態EXEC

1、EXEC命令的括號中只允許包含一個字符串變量,或者一個 字符串文本,或者字符串變量與字符串文本的串聯。不能再括號中使用函數或CASE表達式,如下面嘗試在括號中調用QUOTENAME函數以引用對象名稱,運行將失敗:

   1:  DECLARE @schemaname NVARCHAR(255),@tablename NVARCHAR(128)
   2:  SET @schemaname='dbo'
   3:  SET @tablename='Order Details'
   4:   
   5:  EXEC (N'SELECT COUNT(*) FROM '+QUOTENAME(@schemaname)+N'.'+QUOTENAME(@tablename)+N';')

上述代碼將會產生如下錯誤:

消息 102,級別 15,狀態 1,第 5 行
'QUOTENAME' 附近有語法錯誤。
SQL Server 分析和編譯時間:
CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。

SQL Server 執行時間:
CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。

所以做好的方法是把代碼構造到一個變量中,這樣就不會受限制了,然後再把該變量作爲EXEC命令的輸入參數,就像這樣:

   1:  DECLARE @schemaname NVARCHAR(255) ,
   2:      @tablename NVARCHAR(128) ,
   3:      @sql NVARCHAR(MAX)
   4:  SET @schemaname = 'dbo'
   5:  SET @tablename = 'Order Details'
   6:  SET @sql = N'SELECT COUNT(*) FROM ' + QUOTENAME(@schemaname) + N'.'
   7:      + QUOTENAME(@tablename) + N';'
   8:  EXEC (@sql)

2、EXEC不提供接口。EXEC(<string>)不提供接口。它唯一的輸入就是包含你要調用代碼的字符串。動態批處理不能訪問在調用批處理中定義的局部變量。如下面代碼嘗試訪問定義在調用批處理中的變量將失敗。

   1:  DECLARE @i INT 
   2:  SET @i = 10248
   3:   
   4:  DECLARE @sql NVARCHAR(MAX)
   5:   
   6:  SET @sql = 'SELECT * FROM dbo.Orders WHERE OrderID=@i;'
   7:  EXEC(@sql)

將產生如下錯誤:

消息 137,級別 15,狀態 2,第 1 行
必須聲明標量變量 "@i"。

使用EXEC時,如果想訪問變量,必須把變量內容串聯到動態構建的 代碼字符串中。

DECLARE @i INT 
SET @i = 10248

DECLARE @sql NVARCHAR(MAX)

SET @sql = 'SELECT * FROM dbo.Orders WHERE OrderID='
    + CAST(@i AS NVARCHAR(10)) + ';'
EXEC(@sql)

這樣就沒有問題了。

如果一個變量包含字符串,把該變量的內容串聯到代碼將會導致安全風險(SQL注入),爲了避免SQL注入,可以吧字符串大小限制爲所需的最小長度。當然,實際中這種情況根本不需要動態SQL直接執行SQL語句就可以,這個示例只是爲了演示。

串聯變量的內容存在性能方面的弊端,SQL Server將爲每個唯一的查詢字符串創建新的即席執行計劃,即使查詢模式相同也是這樣的。爲演示這一點,先清空緩存中的執行計劃。

DBCC FREEPROCCACHE
將上端代碼執行三次,分別爲@i賦值10248,10249和10250,然後使用下面的代碼查詢
   1:  SELECT  cacheobjtype ,
   2:          objtype ,
   3:          usecounts ,
   4:          sql
   5:  FROM    sys.syscacheobjects
   6:  WHERE   sql NOT LIKE '%cache%'
   7:          AND sql NOT LIKE '%sys.%'

得到查詢結果:

cacheobjtype objtype usecounts sql
Compiled Plan Adhoc 1 SELECT * FROM dbo.Orders WHERE OrderID=10250;
Compiled Plan Adhoc 1 SELECT * FROM dbo.Orders WHERE OrderID=10248;
Compiled Plan Prepared 3 (@1 smallint)SELECT * FROM [dbo].[Orders] WHERE [OrderID]=@1
Compiled Plan Adhoc 4 SET STATISTICS IO ON SET STATISTICS TIME ON
Compiled Plan Adhoc 1 SELECT * FROM dbo.Orders WHERE OrderID=10249;
Compiled Plan Adhoc 4 SET STATISTICS IO OFF SET STATISTICS TIME OFF

EXEC除了不支持動態批處理中的輸入參數外,也不支持輸出參數。默認情況下,EXEC把查詢輸出返回給調用者。如果你想把輸出結果返回給調用批處理中的變量,事情就沒那麼簡單了,爲此,你需要使用INSERT EXEC把輸出插入到一個目的表,然後再從該表中取值,賦給該變量,就像這樣:

   1:  DECLARE @schemaname NVARCHAR(128) ,
   2:      @tablename NVARCHAR(128) ,
   3:      @colname NVARCHAR(128) ,
   4:      @sql NVARCHAR(MAX) ,
   5:      @cnt INT
   6:      
   7:  SET @schemaname = 'dbo'
   8:  SET @tablename = 'Orders'
   9:  SET @colname = 'CustomerID'
  10:       
  11:  SET @sql = N'SELECT COUNT(DISTINCT ' + QUOTENAME(@colname) + ') FROM '
  12:      + QUOTENAME(@schemaname) + N'.' + QUOTENAME(@tablename) + N';'
  13:      
  14:  CREATE TABLE #T1 ( cnt INT )
  15:  INSERT  INTO #T1
  16:          EXEC ( @sql
  17:              )
  18:  SELECT  @cnt = cnt
  19:  FROM    #T1
  20:  SELECT  @cnt
  21:  DROP TABLE #T1

3、在SQL Server2000中串聯變量值時,EXEC比sp_executesql多一個優勢,它支持更長的代碼,儘管技術上sp_executesql的輸入代碼字符串是NTEXT類型的,但你一般是在局部變量中構造代碼字符串。而你又不能用大型對象類型聲明局部變量,所以,實際上在sp_executesql中執行的查詢字符串被限制爲Unicode字符串(NVARCHAR)支持的最大長度4000,而EXEC支持常規字符串(VARCHAR)允許最大8000個字符。另外EXEC還支持一個特殊的功能,它允許你在括號中串聯多個變量,每個變量都支持8000個字符的長度。

在SQL Server2005中,就不用這麼糾結了,因爲可以爲EXEC命令提供一個VARCHAR(MAX)或NVARCHAR(MAX)的變量作爲輸入,輸入字符串可以達到2GB大小

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章