通过带注释的查询进行 Django 聚合

标签 django orm annotations aggregate

这是我希望 Django 在 SQL 中生成的内容:

select avg(subquery.countval) from (
select count(something) countval,user_id from foo group by user_id
 ) subquery

我认为这应该如何与基于 Annotated 聚合文档的 Django 一起使用:
Foo.objects.all().values('user_id').\
                 annotate(countval=Count('id')).\
                 aggregate(Avg('countval'))

问题是 Django 4.x 没有生成正确的查询。你会得到类似的东西:
SELECT FROM (SELECT foo.user_id as user_id,COUNT(foo.id) 
 AS countval from foo 
 group by foo.user_id)

有任何想法吗?我通过源代码进行了调试,但不清楚出了什么问题。

最佳答案

我无法用纯 Django 代码来做,但这是我能做的最好的,尽可能多地依赖 Django 代码而不是原始 sql。

from django.db import connection
from django.db.models import Count

def get_average_count(klass, field_name):
    foo = klass.objects.values(field_name).annotate(countval=Count('id'))
    query = "SELECT AVG(subquery.countval) FROM (%s) subquery" % str(foo.query)
    cursor = connection.cursor()
    cursor.execute(query)
    return float(cursor.fetchone()[0])

这将执行您说要生成的确切 SQL 语句。它也完全独立于您正在使用的 SQL 后端,并且完全可重用 (yay DRY) 用于具有反向外键或多对多关系的所有类。

如果您真的不想使用原始 SQL,另一种选择是在 Django 中计算平均值:
from __future__ import division # no need to cast to float now

def get_average_count(klass, field_name):
    counts = klass.objects.values(field_name).annotate(countval=Count('id')).\
        values_list('countval', flat=True)
    return reduce(lambda x, y: x + y / len(counts), counts, 0)

如果您计划在数据库中拥有大型数据集,您可能需要检查任何性能差异。

关于通过带注释的查询进行 Django 聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17327823/

相关文章:

python - 用于数据库创建的 Django/postgres 设置,用于运行测试

go - 无法将类型字符串用作 sql.NullString

java - 使用不同的参数运行参数化的 JUnit 测试

python - Django Rest Framework - 嵌套序列化未按预期工作

python - Django 自定义注册字段

python - 仅加载站点首页的 Django 静态文件

python - 在 SQLAlchemy 中,如何在具有多个关系时访问查询中的引用属性?

java - Hibernate OneToMany 映射 - 实体集保存在哪里?

java - springboot 生成的 JSON 中缺少名称标签

c# - 是 C#'s attributes better designed than Java' 的注释吗?