首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL 子查詢 (Subquery) 完整教學:巢狀 SELECT 語法、常見錯誤與 IN/EXISTS 最佳實踐

     編輯:關於SqlServer

什麼是子查詢?

子查詢是嵌套在另一個 SQL 陳述式內部的 `SELECT` 敘述,可出現在 `WHERE`、`FROM`、`SELECT` 清單、`HAVING` 等子句中。外部查詢稱為主查詢,內部稱為子查詢。

WHERE 子句中的子查詢 (最常見)

-- 找出與 Brazil 同區域的國家
SELECT name FROM bbc 
WHERE region = (SELECT region FROM bbc WHERE name = 'Brazil');

致命錯誤:子查詢傳回多列

若 `bbc` 表有兩筆 `name='Brazil'` (不同區域),子查詢回傳兩個區域值,外部 `region = (...)` 會拋錯:`Subquery returns more than 1 value`。

解決方案:使用 IN / EXISTS

-- IN:集合成員資格測試
SELECT name FROM bbc 
WHERE region IN (SELECT region FROM bbc WHERE name = 'Brazil');

-- EXISTS:存在性測試 (通常效能較好,尤其 NOT EXISTS)
SELECT name FROM bbc 
WHERE EXISTS (SELECT 1 FROM bbc b2 WHERE b2.name = 'Brazil' AND b2.region = bbc.region);

實戰範例

範例 1:人口數超過 Russia 的國家

SELECT name FROM bbc 
WHERE population > (SELECT population FROM bbc WHERE name = 'Russia');

範例 2:India 與 Iran 所在區域的所有國家資訊

SELECT * FROM bbc 
WHERE region IN (SELECT region FROM bbc WHERE name IN ('India','Iran'));

範例 3:人均 GDP 超過 United Kingdom 的歐洲國家

SELECT name FROM bbc 
WHERE region = 'Europe' 
  AND gdp/population > (SELECT gdp/population FROM bbc WHERE name = 'United Kingdom');

子查詢類型與效能特性

類型位置回傳優化提示
純量子查詢SELECT 清單、WHERE (單值比較)單一值確保唯一索引/約束,避免多列錯誤
資料列子查詢WHERE (IN/EXISTS)單欄多列優先用 EXISTS (相關) 或 JOIN 改寫
資料表子查詢 (衍生表)FROM多欄多列加上別名,考慮物化為 CTE/暫存表
相關子查詢WHERE/SELECT (參考外部欄位)依外部列執行易造成 Nested Loops 慢查詢,改寫為 JOIN/APPLY

IN vs EXISTS 效能選擇指南

  • 外部大、內部小 → `IN` (或 `JOIN`) 較好:內部結果集小,適合建立雜湊/合併聯結。
  • 外部小、內部大 → `EXISTS` 較好:對每外部列探測內部索引,遇真即停。
  • NOT IN vs NOT EXISTS:**永遠用 `NOT EXISTS`**。`NOT IN` 遇內部含 `NULL` 會回傳空集合 (三值邏輯),且難優化。`NOT EXISTS` 語意清晰、可用反連結。

改寫技巧:子查詢 → JOIN / APPLY

-- 相關子查詢 (慢)
SELECT c.CustomerName, 
       (SELECT MAX(OrderDate) FROM Orders o WHERE o.CustomerID = c.CustomerID) AS LastOrder
FROM Customers c;

-- 改寫為 OUTER APPLY (SQL Server 專用,語意相同但可回多欄)
SELECT c.CustomerName, oa.LastOrder
FROM Customers c
OUTER APPLY (
    SELECT MAX(OrderDate) AS LastOrder FROM Orders o WHERE o.CustomerID = c.CustomerID
) oa;

-- 或 LEFT JOIN 衍生表
SELECT c.CustomerName, d.LastOrder
FROM Customers c
LEFT JOIN (
    SELECT CustomerID, MAX(OrderDate) AS LastOrder FROM Orders GROUP BY CustomerID
) d ON c.CustomerID = d.CustomerID;

結語

子查詢表達力強但易寫出效能陷阱。掌握「純量單值 vs 集合」、「相關 vs 非相關」、「IN/EXISTS 選擇」、「改寫為 JOIN/APPLY」四大維度,即可寫出正確且高效的 SQL。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved