Django ORM如何不使用.raw()实现窗口函数子查询后分组聚合
问题解决方法
错误原因
你遇到的报错是因为Django ORM默认会将连续的annotate操作合并到同一个SELECT查询层级,导致窗口函数被嵌套进聚合函数的参数中,这不符合SQL的执行逻辑:聚合函数在GROUP BY阶段执行,而窗口函数的执行时机晚于聚合,因此不允许在聚合函数内调用窗口函数。
正确实现方式
我们可以通过将窗口函数逻辑封装为子查询,再在外层执行分组聚合的方式实现需求,全程不需要调用.raw(),示例代码如下:
from django.db.models import F, Max, Avg, Window from django.db.models.query import QuerySet # 1. 构造内层子查询:按part_id分区计算max_count inner_qs = TableOne.objects.annotate( max_count=Window( expression=Max('count'), partition_by=F('part_id') # 此处order_by和partition_by字段一致,可省略不写 ) ).values('col_id', 'max_count') # 仅保留后续聚合需要的字段 # 2. 外层查询:基于子查询结果分组求平均值 result_qs = QuerySet(model=TableOne).from_queryset(inner_qs, alias='inner_temp')\ .values('col_id')\ .annotate(avg_max_count=Avg('max_count')) # 执行查询获取结果,返回格式为 [{'col_id': xx, 'avg_max_count': xx}, ...] result = list(result_qs)
实现说明
from_queryset方法会将内层查询作为临时表嵌套进外层查询的FROM子句中,完全匹配你给出的原生SQL结构- 如果你需要关联查询
Col表的其他字段,可以在外层查询中使用col_id__name这类跨表字段名访问 - 该方案所有逻辑都在数据库层面执行,性能和原生SQL基本一致,适合大数据量场景
内容的提问来源于stack exchange,提问作者Andy Pang
相关产品推荐
相关产品推荐

