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
相关产品推荐
相关产品推荐

