列子查詢回傳「單列多行」,行子查詢回傳「單行多列」。兩者配合 IN、ANY、ALL、行構造符 (a,b) 能表達豐富的集合與元組比較邏輯。本文系統講解語法、執行語義、特殊情況 (NULL/空集合) 與優化策略。
SELECT * FROM article WHERE uid IN (SELECT uid FROM user WHERE status = 1);
子查詢回傳 uid 列的多行值集合。
IN 等價於 = ANY:滿足任一值即真。NOT IN 等價於 <> ALL:不等於所有值才真。NULL → NOT IN 整體判斷為 UNKNOWN,**回傳 0 行**。WHERE uid NOT IN (SELECT uid FROM user WHERE status=1 AND uid IS NOT NULL) 或改用 NOT EXISTS。-- 找出薪資大於任一部門平均薪資的員工 SELECT * FROM employee e WHERE salary > ANY (SELECT AVG(salary) FROM employee GROUP BY dept_id);
> ANY:大於**最小值**即真。= ANY:等同 IN。<> ANY:不等於任一值 (注意非 NOT IN)。-- 找出薪資大於所有部門平均薪資的員工 SELECT * FROM employee e WHERE salary > ALL (SELECT AVG(salary) FROM employee GROUP BY dept_id);
> ALL:大於**最大值**才真。< ALL:小於**最小值**才真。= ALL:等於所有值 (集合單一值時才可能真)。<&> ALL:等同 NOT IN。| 子查詢結果 | > ANY | > ALL | IN | NOT IN |
|---|---|---|---|---|
| 空集合 | FALSE | TRUE | FALSE | TRUE |
| 含 NULL (如 1, NULL, 3) | 1 > 1? FALSE; 1 > NULL? UNKNOWN → FALSE | 1 > 1? FALSE; 1 > NULL? UNKNOWN → UNKNOWN | 1 IN (1,NULL) → TRUE | 1 NOT IN (1,NULL) → UNKNOWN (0 行) |
回傳 **單行多列**,外層使用行構造符 (col1, col2, ...) 或 ROW(col1, col2, ...) 進行元組比較。
-- 找出 article 中 title、content、uid 完全等同於 blog 某行的文章
SELECT * FROM article
WHERE (title, content, uid) = (
SELECT title, content, uid FROM blog WHERE bid = 2
);
=、<>、<、>、<=、>= 皆支援行比較 (字典序)。IN 也支援行元組:WHERE (a,b) IN (SELECT c,d FROM t)。LIMIT 1 或唯一索引查找),否則報錯 Subquery returns more than 1 row。| 寫法 | 優化器策略 | 建議索引 |
|---|---|---|
IN (SELECT ...) | 半連接 → 物化臨時表 + 索引查找 | 子查詢 WHERE 條件列 + 選擇列建立複合索引 |
EXISTS (SELECT ...) | 短路掃描,逐外層行索引查找 | 內表關聯列 + WHERE 條件列 |
ANY/ALL | 通常物化子查詢結果集 | 子查詢聚合列建索引 (如 idx_dept_avg_salary) |
INDEX (title, content, uid) 可支援 WHERE (title, content, uid) = ...。-- 原始:薪資大於所有部門平均 WHERE salary > ALL (SELECT AVG(salary) FROM employee GROUP BY dept_id) -- 優化:預先計算最大部門平均 WHERE salary > (SELECT MAX(avg_sal) FROM (SELECT AVG(salary) AS avg_sal FROM employee GROUP BY dept_id) t); -- 或使用 CTE WITH dept_avg AS (SELECT AVG(salary) AS avg_sal FROM employee GROUP BY dept_id) SELECT * FROM employee WHERE salary > (SELECT MAX(avg_sal) FROM dept_avg);
優勢:避免 ALL 逐行比較,轉為單一標量比較,索引友善。
| 錯誤 | 現象 | 修正 |
|---|---|---|
NOT IN 含 NULL | 回傳空結果 | 改用 NOT EXISTS 或過濾 NULL |
| 行子查詢回傳多行 | ERROR 1242 | 加 LIMIT 1、唯一鍵查找、或改用 IN |
| 行構造符列數不匹配 | ERROR 1241 | 對齊 SELECT 清單與外層元組 |
ANY/ALL 無比較運算子 | 語法錯誤 | 必須寫 > ANY、< ALL 等 |
結語: 列子查詢與行子查詢是 SQL 表達集合與元組邏輯的標準工具。理解 IN/ANY/ALL 的真值語義 (特別是 NULL 與空集合)、行構造符的索引匹配規則,並善用 EXISTS、聚合預計算、CTE 改寫,即能寫出語意清晰且高效的查詢。