-- 建立測試環境
CREATE TABLE SubQueryBug (ID1 INT);
INSERT INTO SubQueryBug VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
CREATE TABLE SubQueryBug2 (ID2 INT);
INSERT INTO SubQueryBug2 VALUES (1);
-- 直接查詢:預期報錯
SELECT ID1 FROM SubQueryBug2; -- Msg 207, Invalid column name 'ID1'
-- 子查詢中使用外層同名欄位:卻不報錯!
SELECT * FROM SubQueryBug WHERE ID1 IN (SELECT ID1 FROM SubQueryBug2);
-- 結果:傳回 SubQueryBug 全部 10 筆資料!
-- 若改為 DELETE
DELETE FROM SubQueryBug WHERE ID1 IN (SELECT ID1 FROM SubQueryBug2);
-- 後果:SubQueryBug 全表資料被清空!
SQL Server 解析子查詢欄位時,遵循由內而外的作用域規則:
FROM 子句資料表中尋找欄位。ID1,因此子查詢的 ID1 解析為外層參考。等價於:
SELECT * FROM SubQueryBug sqb
WHERE sqb.ID1 IN (SELECT sqb.ID1 FROM SubQueryBug2);
因為 SubQueryBug2 有 1 列,子查詢傳回外層該列的 ID1 值(10 筆皆為各自的 ID1),IN 條件恆成立,導致全表命中。
-- 正確寫法:明確指定來源
SELECT * FROM SubQueryBug AS a
WHERE a.ID1 IN (SELECT b.ID1 FROM SubQueryBug2 AS b); -- b 無 ID1,直接報錯
-- 或使用 EXISTS(語意更清晰、效能通常更好)
SELECT * FROM SubQueryBug AS a
WHERE EXISTS (SELECT 1 FROM SubQueryBug2 AS b WHERE b.ID2 = a.ID1);
AS t)。t.col)。EXISTS / NOT EXISTS,避免 IN 配合同名欄位的陷阱。Tools → Options → Text Editor → Transact-SQL → IntelliSense → Enable,輸入別名後自動提示欄位,減少手誤。-- 找出沒有對應訂單的客戶
SELECT c.CustomerID, c.CustomerName
FROM Customers c
WHERE NOT EXISTS (SELECT 1 FROM Orders o WHERE o.CustomerID = c.CustomerID);
這是標準用法,關鍵在於明確指定外部參考來源,而非依賴隱含解析。