百萬級大表 big_table 需找出 name 重複的所有記錄 id,直接用 IN 子查詢極慢:
-- 效能極差,MySQL 不會將子查詢物化為臨時表
SELECT id, name FROM big_table
WHERE name IN (SELECT name FROM big_table GROUP BY name HAVING COUNT(*) > 1);
SELECT name, COUNT(*) AS cnt
FROM big_table
GROUP BY name
HAVING cnt > 1
ORDER BY cnt DESC;
CREATE TABLE tmp_dup_names AS
SELECT name FROM big_table
GROUP BY name
HAVING COUNT(*) > 1;
-- 可加索引加速後續 JOIN
ALTER TABLE tmp_dup_names ADD INDEX idx_name (name);
SELECT a.id, a.name
FROM big_table a
INNER JOIN tmp_dup_names t ON a.name = t.name;
SELECT DISTINCT a.id, a.name
FROM big_table a
INNER JOIN tmp_dup_names t ON a.name = t.name;
INNER JOIN 走 ref 存取類型,複雜度從 O(N×M) 降為 O(N+M)。MySQL 8.0+ 支援 CTE,可簡化為單一 SQL:
WITH dup_names AS (
SELECT name FROM big_table GROUP BY name HAVING COUNT(*) > 1
)
SELECT a.id, a.name
FROM big_table a
JOIN dup_names d ON a.name = d.name;
優化器會自動物化 CTE,效能等同手動臨時表,碼更乾淨。
DROP TABLE tmp_dup_names;
大表重複值查詢,拒絕直接 IN 子查詢。採用「建立重複值集合臨時表 → 加索引 → 主表 JOIN 臨時表」三步驟,或 MySQL 8.0 CTE 寫法,可將分鐘級查詢降為秒級,是 DBA 必備技巧。