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

Django ORM使用distinct与不同order_by报错,求获取最近发信联系人方案

解决PostgreSQL下Django distinct与order_by冲突的问题

问题原因

你遇到的错误是PostgreSQL的DISTINCT ON语法限制导致的:它要求ORDER BY的第一个字段必须和DISTINCT ON指定的字段完全一致,所以直接按-created_at排序再distinct('contact_id')会触发报错。

可行解决方法

方法1:调整排序顺序后二次排序

先按contact_id排序,再按created_at倒序,这样distinct('contact_id')会保留每个联系人的最新消息,之后再重新按时间倒序取前10:

latest_messages = Message.objects.order_by('contact_id', '-created_at').distinct('contact_id').order_by('-created_at')[:10]

Django会合并两次order_by逻辑,最终SQL先按contact_id、-created_at排序去重,再重新按-created_at排序取前10,刚好满足获取最近10个联系人的需求。

方法2:子查询获取每个联系人的最新消息时间

通过子查询先拿到每个联系人的最新消息时间,再筛选对应消息:

from django.db.models import Subquery, OuterRef, Max

# 子查询:获取每个contact_id的最新消息时间
max_created_subquery = Message.objects.filter(contact_id=OuterRef('contact_id')).values('contact_id').annotate(max_created=Max('created_at')).values('max_created')

# 筛选出每个联系人的最新消息,按时间倒序取前10
latest_messages = Message.objects.filter(created_at=Subquery(max_created_subquery)).order_by('-created_at')[:10]

这种方法逻辑更清晰,适合需要明确筛选条件的复杂场景。

方法3:利用自增ID获取最新消息(如果ID是自增的)

如果你的Message表id是自增主键,最新的消息ID对应最新的创建时间,可以用聚合函数先拿到每个联系人的最新消息ID:

from django.db.models import Max

# 获取每个contact_id对应的最新消息ID
latest_msg_ids = Message.objects.values('contact_id').annotate(latest_msg_id=Max('id')).values('latest_msg_id')

# 根据ID筛选消息并排序
latest_messages = Message.objects.filter(id__in=latest_msg_ids).order_by('-created_at')[:10]

这种方法性能通常更好,因为主键查询的效率更高。

注意事项

  • 如果同一个联系人在同一时间发送了多条消息,方法2和3可能返回多条该联系人的消息,此时可以在最后加上.distinct('contact_id')去重。
  • 以上方法针对PostgreSQL优化,其他数据库(如MySQL)的去重逻辑不同,需要调整写法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 02:31:08