| 特性 | SET @Var = (SELECT ...) | SELECT @Var = Col FROM ... |
|---|---|---|
| 返回行數 = 0 | @Var = NULL | @Var 保持原值 (極其危險) |
| 返回行數 = 1 | 正常賦值 | 正常賦值 |
| 返回行數 > 1 | 報錯 Msg 512:子查詢返回多於 1 個值 | 靜默截取最後一行,無任何警告 |
| ANSI 標準 | 符合 SQL-92 | T-SQL 擴展 |
| 可讀性 | 明確標量語義 | 易混淆為結果集查詢 |
CREATE TABLE dbo.SetTest (PK INT PRIMARY KEY, Name VARCHAR(30));
INSERT dbo.SetTest VALUES (1,'Alice'), (2,'Bob'), (3,'Alice'); -- 兩個 Alice
GO
-- 1. SELECT 賦值:靜默取最後一行 PK=3
DECLARE @V1 INT;
SELECT @V1 = PK FROM dbo.SetTest WHERE Name = 'Alice';
PRINT 'SELECT result: ' + CAST(@V1 AS VARCHAR(10)); -- 輸出 3,業務邏輯可能預期 1 或報錯
-- 2. SET 賦值:直接報錯,暴露數據質量問題
DECLARE @V2 INT;
BEGIN TRY
SET @V2 = (SELECT PK FROM dbo.SetTest WHERE Name = 'Alice');
END TRY
BEGIN CATCH
PRINT 'SET Error: ' + ERROR_MESSAGE(); -- Msg 512, Level 16, Subquery returned more than 1 value
END CATCH
GO
DROP TABLE dbo.SetTest;
GO
-- 錯誤模式:動態 SQL 內部 SELECT 賦值給外部變量 (作用域隔離,實際無效)
DECLARE @Outer INT;
EXEC sp_executesql N'SELECT @Inner = PK FROM dbo.SetTest WHERE Name = ''Alice''', N'@Inner INT OUTPUT', @Outer OUTPUT;
-- @Outer 仍為 NULL,因為 @Inner 是內部變量
-- 正確模式:sp_executesql 輸出參數
DECLARE @Outer INT;
EXEC sp_executesql N'SET @Outer = (SELECT PK FROM dbo.SetTest WHERE Name = ''Alice'')', N'@Outer INT OUTPUT', @Outer OUTPUT;
-- 此處若多行會正確報錯
`SET` 是**防禦性編程**的體現:用編譯期/運行期報錯替換靜默數據損壞。在關鍵業務邏輯 (計費、庫存、權限) 中,任何標量子查詢賦值**必須**使用 `SET`,並配合 `TRY/CATCH` 處理異常,將數據質量問題在最早環節攔截。