You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中使用order_by+distinct后按id排序报错的解决求助

问题:按SKU去重并保留最小ID后按ID排序

现有数据表:

id sku
1  C 
2  B
3  C
4  A

期望得到的结果:

1 C
2 B
4 A

最初尝试使用distinct去重:

queryset = self.filter_queryset(queryset.order_by('sku').distinct('sku'))

得到的结果是:

4 A
1 C
2 B

之后尝试按ID排序:

queryset = queryset.order_by('id')

触发了如下错误:

django.db.utils.ProgrammingError: SELECT DISTINCT ON expressions must match initial ORDER BY expressions
LINE 1: SELECT DISTINCT ON ("defapp_log"."sku") "d...

需要实现按SKU去重后保留最小ID的数据,并最终按ID排序的需求。

解决方案

原因分析

你遇到的错误是PostgreSQL的DISTINCT ON语法限制导致的:使用DISTINCT ON (字段)时,SQL查询的ORDER BY子句必须以该字段作为起始排序项,后续无法直接修改排序规则(比如改为按ID排序)。

方法一:聚合查询获取最小ID后筛选

通过聚合函数先获取每个SKU对应的最小ID,再基于这些ID查询完整记录并排序,这是最稳妥的方案:

from django.db.models import Min

# 第一步:获取每个sku对应的最小id值
min_id_list = self.filter_queryset(queryset).values('sku').annotate(min_id=Min('id')).values_list('min_id', flat=True)

# 第二步:筛选出这些id的记录并按id排序
queryset = self.filter_queryset(queryset.filter(id__in=min_id_list)).order_by('id')

方法二:调整排序顺序后再处理(PostgreSQL专属)

如果你的数据库是PostgreSQL,可以调整order_by的顺序,先按sku和id排序(确保每个SKU下最小ID的记录排在最前面),再执行distinct('sku'),最后在Python层面重新排序(避免数据库层面的限制):

# 先按sku和id升序排序,distinct后保留每个sku的最小id记录
queryset = self.filter_queryset(queryset.order_by('sku', 'id').distinct('sku'))
# 在Python层面按id排序(注意:数据量较大时建议用方法一)
queryset = sorted(queryset, key=lambda x: x.id)

方法三:使用子查询(大数据量场景更高效)

对于大数据量场景,子查询可以减少数据传输:

from django.db.models import Subquery, OuterRef

# 子查询:获取当前sku对应的最小id
subquery = self.filter_queryset(queryset.filter(sku=OuterRef('sku'))).values('id').order_by('id')[:1]

# 主查询:筛选出id等于子查询结果的记录,并按id排序
queryset = self.filter_queryset(queryset.filter(id=Subquery(subquery))).order_by('id')

内容的提问来源于stack exchange,提问作者whitebear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:22:38