業務常見需求:有兩張結構相似的表 t_appinfo(主表)和 g_appinfo(備表),均含 package_name 字段。需要找出 在 t_appinfo 存在、但 g_appinfo 缺失 的記錄。
SELECT a.name, a.package_name
FROM t_appinfo a
WHERE NOT EXISTS (
SELECT 1 FROM g_appinfo g WHERE a.package_name = g.p_packagename
)
LIMIT 10000;
SELECT a.name, a.package_name
FROM t_appinfo a
WHERE a.package_name NOT IN (
SELECT g.p_packagename FROM g_appinfo g
)
LIMIT 50000;
⚠ 當子查詢結果集包含 NULL 時,NOT IN 將返回空結果集!
原理:value NOT IN (1, 2, NULL)等價於value != 1 AND value != 2 AND value != NULL,最後一個條件永遠為 UNKNOWN,導致整體為 FALSE。
WHERE p_packagename IS NOT NULL當兩表均超 10 萬行,必須為 package_name / p_packagename 建立索引:
ALTER TABLE t_appinfo ADD INDEX idx_pkg_name (package_name);
ALTER TABLE g_appinfo ADD INDEX idx_p_pkg_name (p_packagename);
EXPLAIN SELECT ... WHERE NOT EXISTS (...);
關鍵指標:
type 列顯示 ref 或 eq_ref 表示索引生效rows 估算掃描行數應遠小於全表行數Extra 出現 Using where; Not exists 為最佳SELECT a.name, a.package_name
FROM t_appinfo a
LEFT JOIN g_appinfo g ON a.package_name = g.p_packagename
WHERE g.p_packagename IS NULL;
在某些舊版本 MySQL 中,此寫法優化器處理更穩定;新版本(5.7+)三種寫法經優化器重寫後性能幾乎一致。
LIMIT 分頁導出,避免長事務阻塞