子查詢按回傳結果形態分為**標量子查詢**(單值)、**列子查詢**(單列多行)、**行子查詢**(單行多列)、**表子查詢**(多行多列)。本文聚焦標量子查詢與通用子查詢的比較運算、EXPLAIN 執行計畫分析,並給出演示資料與優化實戰。
回傳 **單一值** (一行一列) 的子查詢,可用於 =、>、<、>=、<=、<> 比較。
-- 找出最新註冊用戶的所有文章
SELECT * FROM article
WHERE uid = (
SELECT uid FROM user
WHERE status = 1
ORDER BY regdate DESC
LIMIT 1
);
Subquery returns more than 1 row)。LIMIT 1、聚合函數 (MAX、MIN) 保證單值。NULL,比較結果為 UNKNOWN,外層不匹配任何行。| 運算子 | 適用子查詢類型 | 說明 |
|---|---|---|
=、<>、>、<、>=、<= | 標量子查詢 | 單值比較 |
= | 行子查詢 | 元組比較 (a,b) = (SELECT c,d ...) |
IN、NOT IN | 列子查詢、表子查詢 | 集合成員資格 |
ANY/SOME | 列子查詢 | 滿足任一值即真,需配合比較運算子 > ANY |
ALL | 列子查詢 | 滿足所有值才真,需配合比較運算子 < ALL |
EXISTS/NOT EXISTS | 任意子查詢 | 存在性判斷,忽略選擇清單 |
-- 找出發文數 = 最大發文數的作者 SELECT * FROM article a WHERE 2 = (SELECT COUNT(*) FROM article WHERE uid = a.uid); -- 這裡 2 為示例常數 -- 無法用 JOIN 直接表達「計數等於某值」
-- 每位用戶最新一篇文章
SELECT * FROM article a
WHERE created_at = (
SELECT MAX(created_at) FROM article WHERE uid = a.uid
);
| 欄位 | 子查詢相關含義 |
|---|---|
select_type | PRIMARY (外層)、SUBQUERY (獨立子查詢)、DEPENDENT SUBQUERY (關聯子查詢)、DERIVED (FROM 衍生表)、UNCACHEABLE SUBQUERY (含不確定函數) |
type | 子查詢存取類型:ref、eq_ref、index_subquery、unique_subquery |
Extra | Using index (覆蓋索引)、Using where、Using temporary (物化) |
EXPLAIN SELECT * FROM article a WHERE uid = (SELECT uid FROM user WHERE status=1 ORDER BY regdate DESC LIMIT 1);
輸出解讀:
- 第 1 行:select_type=PRIMARY、type=ref、key=idx_uid、rows=10 → 外層用索引查找。
- 第 2 行:select_type=SUBQUERY、type=ref、key=idx_status、rows=1 → 子查詢用索引+LIMIT 1 單次查找。
結論:標量子查詢若有索引支援,效能極佳。
原始 (慢):
SELECT u.*,
(SELECT COUNT(*) FROM article WHERE uid = u.uid) AS article_cnt
FROM user u;
問題:10 萬用戶 → 10 萬次子查詢執行。
改寫 (快):
SELECT u.*, COALESCE(c.cnt, 0) AS article_cnt
FROM user u
LEFT JOIN (
SELECT uid, COUNT(*) AS cnt FROM article GROUP BY uid
) c ON u.uid = c.uid;
原理:子查詢改為衍生表聚合一次,再 LEFT JOIN,僅掃描 article 1 次。
SELECT u.*, a.article_cnt
FROM user u
LEFT JOIN LATERAL (
SELECT COUNT(*) AS article_cnt FROM article WHERE uid = u.uid
) a ON TRUE;
LATERAL 允許右側子查詢引用左側表欄位,語意清晰且優化器可選擇最佳執行策略。
結語: 標量子查詢在「單值比較」、「聚合結果參與過濾」場景不可替代,但「逐行執行」的關聯標量子查詢是效能大坑。掌握 EXPLAIN 識別 DEPENDENT SUBQUERY、type=ALL,優先改寫為 JOIN、LATERAL 或 CTE,讓資料庫做集合運算而非迴圈運算。