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

MySQL DISTINCT、GROUP BY與ORDER BY陷阱深度解析

     編輯:MySQL 資料庫相關知識

問題場景:需求與直覺的衝突

專案需求:從職缺表f_job中,依job_time降序取得最新10筆不重複公司(com_id)的職缺記錄。表結構含job_id(PK)、com_id(FK)、job_time等欄位。

錯誤嘗試與現象分析

嘗試1:單純DISTINCT + ORDER BY

SELECT DISTINCT(com_id) FROM f_job ORDER BY job_time DESC LIMIT 10;

結果錯誤:最新的com_id=2205未出現,被排至後方。原因:DISTINCT先去重,再依job_time排序,但去重後保留哪一筆job_time是不確定的(通常是第一筆遭遇的),導致排序依據失真。

嘗試2:DISTINCT多欄位 + ORDER BY

SELECT DISTINCT(com_id), job_time FROM f_job ORDER BY job_time DESC LIMIT 10;

結果仍錯:同一com_id的不同job_time皆保留,造成重複公司出現。

嘗試3:GROUP BY單欄位

SELECT com_id FROM f_job GROUP BY com_id ORDER BY job_time DESC LIMIT 10;

結果同嘗試1:GROUP BY com_id未指定聚合函數時,job_time取值非確定性(MySQL 5.7+預設ONLY_FULL_GROUP_BY會報錯)。

嘗試4:GROUP BY多欄位

SELECT com_id FROM f_job GROUP BY com_id, job_time ORDER BY job_time DESC LIMIT 10;

改善但未根治:同公司不同時間仍會重複出現。

正確解法:聚合函數配合子查詢或JOIN

核心邏輯:先找出每家公司最新的職缺時間,再關聯回主表取完整資料。

方案一:子查詢法(直觀易懂)

SELECT f.* FROM f_job f
INNER JOIN (
    SELECT com_id, MAX(job_time) AS max_time
    FROM f_job
    GROUP BY com_id
) latest ON f.com_id = latest.com_id AND f.job_time = latest.max_time
ORDER BY f.job_time DESC LIMIT 10;

方案二:視窗函數(MySQL 8.0+)

WITH ranked AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY com_id ORDER BY job_time DESC) AS rn
    FROM f_job
)
SELECT * FROM ranked WHERE rn = 1 ORDER BY job_time DESC LIMIT 10;

核心觀念釐清

  • DISTINCT僅能回傳去重後的目標欄位,無法同時保證其他欄位的正確性
  • GROUP BY未使用聚合函數時,非分組欄位取值是非確定性的
  • ORDER BY在DISTINCT/GROUP BY之後執行,排序依據若已被去重破壞則無意義
  • 需求「分組取最新/最大/最小」必須使用聚合函數(MAX/MIN)或視窗函數
這個案例深刻揭示:SQL語法順序與邏輯順序的差異,是開發者最易踩的坑。理解FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT的執行順序,配合聚合函數思維,才能寫出正確高效的查詢。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved