在 Django 專案開發中,distinct() 是常用的去重查詢方法。然而,許多開發者在使用 MySQL 時會發現 distinct() 行為與 PostgreSQL 不同,甚至報錯。本文深入解析 Django ORM 中 distinct 的正確用法、MySQL 限制原因及解決方案。
Django 提供兩種資料查詢途徑:
filter()、values()、distinct()、order_by() 等模型方法raw() 直接執行 SQL 語句# 正確:查詢不重複的 finish_time
LOrder.objects.values('finish_time').distinct()
MySQL 要求 DISTINCT 與 ORDER BY 欄位一致,因此需加上排序:
# MySQL 相容寫法
LOrder.objects.values('finish_time').distinct().order_by('finish_time')
Django 官方文檔範例多為 PostgreSQL 語法:
# PostgreSQL 支援指定欄位去重
Entry.objects.order_by('pub_date').distinct('pub_date')
Entry.objects.order_by('blog').distinct('blog')
關鍵差異:MySQL 不支援 distinct('field_name') 指定欄位去重語法,僅支援無參數的 distinct()。
LOrder.objects.raw('SELECT DISTINCT id, finish_time FROM keywork_lorder GROUP BY finish_time')
坑 1:主鍵不可省略
Django 要求原始查詢必須包含主鍵欄位(通常是 id),否則拋出InvalidQuery異常:
Raw query must include the primary key坑 2:MySQL 去重需搭配 GROUP BY
MySQL 中SELECT DISTINCT實際執行邏輯等同於GROUP BY,正確寫法:
SELECT DISTINCT id, finish_time FROM keywork_lorder GROUP BY finish_time
| 場景 | 推薦做法 | 說明 |
|---|---|---|
| 單欄位去重 | .values('field').distinct().order_by('field') | ORM 標準寫法,MySQL 相容 |
| 多欄位去重 | .values('field1','field2').distinct().order_by('field1','field2') | 所有 values 欄位需在 order_by 中 |
| 複雜去重邏輯 | 使用 raw() + 原生 SQL | 包含主鍵,搭配 GROUP BY |
| 大量資料去重 | 考慮資料庫層面建立唯一索引 | 從源頭避免重複資料 |
distinct() 必須搭配 order_by()DISTINCT 底層實際執行 GROUP BY 邏輯