在預存程序中動態建構 SQL 字串並透過 EXEC 或 sp_executesql 執行,或呼叫另一個有輸出參數的預存程序,皆需正確捕獲傳回值。本文系統整理兩大場景的正確寫法、參數化技巧與常見錯誤。
DECLARE @SQL NVARCHAR(MAX) = N'SELECT * FROM T WHERE ID = 1';
EXEC (@SQL); -- 無法將結果指派給變數,亦無法參數化,易受注入攻擊
DECLARE @SQL NVARCHAR(MAX) = N'SELECT @Total = SUM(Amount) FROM Orders WHERE CustID = @CustID';
DECLARE @CustID INT = 1001;
DECLARE @Total MONEY;
EXEC sp_executesql
@SQL,
N'@CustID INT, @Total MONEY OUTPUT',
@CustID = @CustID,
@Total = @Total OUTPUT;
SELECT @Total; -- 取得結果
CREATE PROC Proc_CalcBonus
@EmpID INT,
@Bonus MONEY OUTPUT
AS
BEGIN
SELECT @Bonus = CASE WHEN Salary > 50000 THEN Salary * 0.1 ELSE Salary * 0.05 END
FROM Employee WHERE EmpID = @EmpID;
END
DECLARE @MyBonus MONEY;
EXEC Proc_CalcBonus @EmpID = 101, @Bonus = @MyBonus OUTPUT; -- 必須加 OUTPUT
SELECT @MyBonus;
-- 錯誤:漏寫 OUTPUT
EXEC Proc_CalcBonus @EmpID = 101, @Bonus = @MyBonus; -- @MyBonus 仍為 NULL
-- 錯誤:變數宣告型別不符
DECLARE @MyBonus INT; -- 應為 MONEY
EXEC Proc_CalcBonus @EmpID = 101, @Bonus = @MyBonus OUTPUT;
DECLARE @ProcName SYSNAME = N'Proc_CalcBonus'; -- 可能來自設定表
DECLARE @SQL NVARCHAR(MAX) = N'EXEC ' + QUOTENAME(@ProcName) + N' @EmpID = @EmpID, @Bonus = @Bonus OUTPUT';
DECLARE @EmpID INT = 101, @Bonus MONEY;
EXEC sp_executesql @SQL, N'@EmpID INT, @Bonus MONEY OUTPUT', @EmpID, @Bonus OUTPUT;
SELECT @Bonus;
搭配 QUOTENAME 防止物件名稱注入。
| 特性 | RETURN 值 | OUTPUT 參數 |
|---|---|---|
| 資料型別 | 僅 INT | 任意標量型別 |
| 數量 | 單一值 | 可多個 |
| 用途 | 狀態碼(0=成功,非 0=錯誤代碼) | 業務資料回傳 |
| 捕獲方式 | EXEC @Ret = Proc... | EXEC Proc @P = @V OUTPUT |
sp_executesql 而非 EXEC (@Str)。OUTPUT 關鍵字。QUOTENAME 包裹。TRY...CATCH 中,記錄 ERROR_MESSAGE()。