當資料表名、欄位名、資料庫名等識別碼需在執行期由變數決定時,必須使用動態 SQL。本文比較 EXEC (@SQL) 與 sp_executesql 兩種執行方式,說明變數作用域限制、參數化優勢與安全實作模式。
DECLARE @Week INT = 3;
DECLARE @User NVARCHAR(50) = N'User001';
DECLARE @SQL NVARCHAR(MAX);
-- 構建動態欄位名:[Week3]
SET @SQL = N'UPDATE WeekCount SET [' + QUOTENAME(N'Week' + CAST(@Week AS NVARCHAR(10))) + N'] = ['
+ QUOTENAME(N'Week' + CAST(@Week AS NVARCHAR(10))) + N'] + 1 WHERE UserID = @User';
-- 執行並傳入參數
EXEC SP_EXECUTESQL @SQL, N'@User NVARCHAR(50)', @User = @User;
| 特性 | EXEC (@Str) | SP_EXECUTESQL |
|---|---|---|
| 參數化 | 不支援 | 支援輸入/輸出參數 |
| 計畫重用 | 不可重用(每次重新編譯) | 可重用(參數化查詢) |
| 防注入 | 需自行處理 | 參數與程式碼分離 |
| 變數作用域 | 僅看見全域/會話變數 | 可存取區域變數(透過參數) |
| 效能 | 高編譯 CPU | 低編譯 CPU |
DECLARE @Val INT = 100;
EXEC ('PRINT @Val'); -- 錯誤:Must declare the scalar variable "@Val"
解法:改用 SP_EXECUTESQL 透過參數傳遞。
QUOTENAME() 包裹,防止識別碼注入。QUOTENAME(@DBName) + N'.dbo.Table'。EXECUTE 權限於封裝預存程序,不直接授與底層物件權限。DECLARE @SortCol SYSNAME = N'CreateDate';
DECLARE @SortDir VARCHAR(4) = N'DESC';
DECLARE @SQL NVARCHAR(MAX) = N'SELECT * FROM Orders ORDER BY ' + QUOTENAME(@SortCol) + N' ' + @SortDir;
-- @SortDir 需驗證僅為 ASC/DESC,不可直接拼接使用者輸入
EXEC SP_EXECUTESQL @SQL;