DISTINCT 是最常用的去重關鍵字,但「只能返回目標列」、「多列語義誤解」、「性能陷阱」讓許多開發者踩坑。本文系統梳理 DISTINCT 四大實戰模式、等價替代方案、性能優化與避坑指南。
-- 返回去重後的值列表 SELECT DISTINCT `meta_key` FROM `wp_postmeta`; -- 計算去重後行數 SELECT COUNT(DISTINCT `meta_key`) FROM `wp_postmeta`;
適用: 低基數枚舉值(狀態、類型、標籤)、快速檢查數據分布。
-- 錯誤認知:以為只按 meta_key 去重,實際按 (meta_key, post_id) 組合去重 SELECT DISTINCT `meta_key`, `post_id` FROM `wp_postmeta`; -- 正確:只想看 meta_key 去重,但同時想帶出 post_id(任意一行) -- MySQL 允許非聚合列不在 GROUP BY 中(舊版本/關閉 ONLY_FULL_GROUP_BY) SELECT `meta_key`, `post_id` FROM `wp_postmeta` GROUP BY `meta_key`;
核心規則: DISTINCT col1, col2 = GROUP BY col1, col2,**所有列組合唯一才保留**。若需「按某列去重、帶出其他列任意值」,必須用 GROUP BY 或窗口函數。
-- 等價於 DISTINCT meta_key
SELECT `meta_key` FROM `wp_postmeta` GROUP BY `meta_key`;
-- 進階:去重同時統計出現次數
SELECT `meta_key`, COUNT(*) AS cnt FROM `wp_postmeta` GROUP BY `meta_key` ORDER BY cnt DESC;
-- 進階:去重同時取最新記錄的其他欄位(需排序保證)
SELECT `meta_key`, `meta_value`
FROM (
SELECT `meta_key`, `meta_value` FROM `wp_postmeta` ORDER BY `post_id` DESC
) t GROUP BY `meta_key`;
-- 按 meta_key 分組,按 post_id 降序編號,取第一行(最新)
WITH ranked AS (
SELECT
`meta_key`, `meta_value`, `post_id`,
ROW_NUMBER() OVER (PARTITION BY `meta_key` ORDER BY `post_id` DESC) AS rn
FROM `wp_postmeta`
)
SELECT `meta_key`, `meta_value`, `post_id` FROM ranked WHERE rn = 1;
優勢: 明確控制「保留哪一行」(最新/最舊/最大/最小),語義零歧義,執行計劃優於舊式子查詢。
| 方案 | 執行機制 | 適用場景 | 優化建議 |
|---|---|---|---|
| DISTINCT | 內部臨時表 + 排序去重 | 單列、小結果集 | 加索引 INDEX(meta_key) 避免 filesort |
| GROUP BY | 同 DISTINCT,可攜帶聚合 | 需統計/帶出其他列 | 同上,覆蓋索引 INDEX(meta_key, meta_value) |
| ROW_NUMBER() | 窗口函數,單掃描 + 排序 | 精確控制保留行、大數據 | 分區剪枝 + 索引順序匹配 ORDER BY |
| GROUP_CONCAT(DISTINCT ...) | 聚合字符串 | 需將去重值拼成單字符串 | 注意 group_concat_max_len (預設 1024) |