萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL LEFT JOIN 查詢差集:找出 A 表有而 B 表無的記錄

核心模式:LEFT JOIN + IS NULL


SELECT A.*
FROM `A` 
LEFT JOIN `B` ON A.pid = B.pid
WHERE B.pid IS NULL;

原理圖解


A 表 (主表)          B 表 (從表)          LEFT JOIN 結果
pid | data          pid | info          A.pid | A.data | B.pid | B.info
----+-----          ----+-----          ------+--------+-------+-------
1   | a1            1   | b1            1     | a1     | 1     | b1
2   | a2            3   | b3            2     | a2     | NULL  | NULL  ← 目標行
3   | a3                          3     | a3     | 3     | b3
4   | a4                          4     | a4     | NULL  | NULL  ← 目標行
  • LEFT JOIN 保證左表 (A) 所有行輸出。
  • 右表 (B) 無匹配行時,其列填充 NULL。
  • WHERE B.pid IS NULL 精確過濾出「A 有、B 無」的差集。

實戰案例:抽獎系統 - 找出未中獎用戶


-- 參與表:一用戶多條記錄
CREATE TABLE `wall_list` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `openid` VARCHAR(64),
  `weid` INT
);

-- 中獎表
CREATE TABLE `wall_lottery` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `openid` VARCHAR(64)
);

-- 查詢 weid=63 活動中未中獎的用戶 (去重 openid)
SELECT DISTINCT l.`openid`
FROM `wall_list` AS l
LEFT JOIN `wall_lottery` AS lottery ON l.`openid` = lottery.`openid`
WHERE l.`weid` = 63
  AND lottery.`id` IS NULL;

關鍵知識點

  1. LEFT JOIN 語義:左表全量,右表匹配或 NULL。
  2. 判斷欄位選擇:用右表主鍵或非空列做 IS NULL 判斷,避免業務數據本身含 NULL 導致誤判。
  3. 去重:一對多關聯時,左表一行匹配右表多行會展開,DISTINCT 或 GROUP BY 去重。
  4. 性能:右表關聯列 (B.pid) 必須有索引,否則全表掃描 B 表,極慢。

對比:NOT IN / NOT EXISTS

寫法示例優點缺點/風險
LEFT JOIN + IS NULLSELECT A.* FROM A LEFT JOIN B ON A.id=B.id WHERE B.id IS NULL標準 SQL、可選右表列、優化器友好需注意右表列非空性
NOT INSELECT * FROM A WHERE id NOT IN (SELECT id FROM B)語意直觀子查詢結果含 NULL 會導致全空結果集、優化器難優化
NOT EXISTSSELECT * FROM A WHERE NOT EXISTS (SELECT 1 FROM B WHERE A.id=B.id)語義清晰、無 NULL 陷阱、支援相關子查詢舊版本優化器可能轉為 Dependent Subquery 慢

推薦:NOT EXISTS(語義最清晰、無 NULL 坑、現代優化器優秀)


SELECT A.*
FROM `A`
WHERE NOT EXISTS (
  SELECT 1 FROM `B` WHERE B.pid = A.pid
);

擴展:求交集、對稱差集


-- 交集 (A ∩ B)
SELECT A.* FROM A INNER JOIN B ON A.pid = B.pid;

-- 對稱差集 (A Δ B) = (A-B) ∪ (B-A)
SELECT A.* FROM A LEFT JOIN B ON A.pid=B.pid WHERE B.pid IS NULL
UNION ALL
SELECT B.* FROM B LEFT JOIN A ON B.pid=A.pid WHERE A.pid IS NULL;
copyright © 萬盛學電腦網 all rights reserved