首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 子查詢與標量子查詢完整教學:語法、比較運算、EXPLAIN 分析與優化案例

     編輯:MySQL 資料庫相關知識

前言

子查詢按回傳結果形態分為**標量子查詢**(單值)、**列子查詢**(單列多行)、**行子查詢**(單行多列)、**表子查詢**(多行多列)。本文聚焦標量子查詢與通用子查詢的比較運算、EXPLAIN 執行計畫分析,並給出演示資料與優化實戰。

一、標量子查詢

1.1 定義

回傳 **單一值** (一行一列) 的子查詢,可用於 =、>、<、>=、<=、<> 比較。

1.2 語法範例

-- 找出最新註冊用戶的所有文章
SELECT * FROM article
WHERE uid = (
    SELECT uid FROM user
    WHERE status = 1
    ORDER BY regdate DESC
    LIMIT 1
);

1.3 執行流程

  1. 執行子查詢,得到單一值 (若回傳多行 → 報錯 Subquery returns more than 1 row)。
  2. 外層查詢將該值作為常數參與比較。

1.4 關鍵限制

  • **不可回傳多行**;需配合 LIMIT 1、聚合函數 (MAX、MIN) 保證單值。
  • **不可為關聯子查詢** 回傳多行 (但可關聯回傳單值)。
  • 若子查詢回傳 NULL,比較結果為 UNKNOWN,外層不匹配任何行。

二、子查詢比較運算

運算子適用子查詢類型說明
=、<>、>、<、>=、<=標量子查詢單值比較
=行子查詢元組比較 (a,b) = (SELECT c,d ...)
IN、NOT IN列子查詢、表子查詢集合成員資格
ANY/SOME列子查詢滿足任一值即真,需配合比較運算子 > ANY
ALL列子查詢滿足所有值才真,需配合比較運算子 < ALL
EXISTS/NOT EXISTS任意子查詢存在性判斷,忽略選擇清單

三、子查詢 vs JOIN:必須用子查詢的場景

3.1 聚合結果參與比較

-- 找出發文數 = 最大發文數的作者
SELECT * FROM article a
WHERE 2 = (SELECT COUNT(*) FROM article WHERE uid = a.uid);  -- 這裡 2 為示例常數
-- 無法用 JOIN 直接表達「計數等於某值」

3.2 關聯子查詢中的聚合

-- 每位用戶最新一篇文章
SELECT * FROM article a
WHERE created_at = (
    SELECT MAX(created_at) FROM article WHERE uid = a.uid
);

四、EXPLAIN 分析子查詢

4.1 關鍵欄位

欄位子查詢相關含義
select_typePRIMARY (外層)、SUBQUERY (獨立子查詢)、DEPENDENT SUBQUERY (關聯子查詢)、DERIVED (FROM 衍生表)、UNCACHEABLE SUBQUERY (含不確定函數)
type子查詢存取類型:ref、eq_ref、index_subquery、unique_subquery
ExtraUsing index (覆蓋索引)、Using where、Using temporary (物化)

4.2 實戰分析

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 單次查找。
結論:標量子查詢若有索引支援,效能極佳。

五、優化案例:標量子查詢改寫為 JOIN

原始 (慢):

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 次。

六、MySQL 8.0 新特性:側寫連接

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,讓資料庫做集合運算而非迴圈運算。

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