浅析django数据库的aggregate与annotate聚合

本文介绍了Django中aggregate和annotate这两种高级查询方法,用于在Task数据表中高效处理多条记录的聚合操作,如求平均值。aggregate返回字典形式的结果,而annotate则对QuerySet中的每个对象进行注解并保持QuerySet结构。
摘要由CSDN通过智能技术生成

背景

        aggregate与annotate聚合作为django的高级查询方法,解决了常规查询方法效率低的问题。它面向的场景通常类似于:Task数据表内存在多条记录,每条记录内包含了一个叫做segment_size的整数型字段,如我们想统计该信息在所有Task上的平均值,常规的解决办法是query到所有记录,然后for循环处理。

        对于聚合操作,django提供了两种方法,一种是QuerySet的所有项(objects.all())直接聚合,另一种是先“分组”再聚合

aggregate聚合

        aggregate聚合操作的返回值是字典,字典内聚合了不同查询对象的聚合操作结果。django的aggregate方法支持的聚合方式包括AVG、COUNT、MAX、MIN、SUM。操作示例:

>>> from django.db.models import Avg
>>> from cvat.apps.engine.models import Project, Task
>>> Task.objects.aggregate(Avg('overlap'))
{'overlap__avg': 0.0}
>>> Task.objects.aggregate(Avg('overlap'), Avg(''segment_size))
  File "<console>", line 1
    Task.objects.aggregate(Avg('overlap'), Avg(''segment_size))
                                               ^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
>>> Task.objects.aggregate(Avg('overlap'), Avg('segment_size'))
{'overlap__avg': 0.0, 'segment_size__avg': 1.0}

        aggregate聚合操作默认返回值得key命名规则为:字段__操作方法,也可以通过以下方法自定义该key的名称,即:

Task.objects.aggregate(overlap_average=Avg('overlap'))

annotate聚合

        annotate进行聚合的原理是将QuerySet内的每个对象都进行注解,注解的信息直接存储为对象的一个新字段,因此其查询的返回值仍然是QuerySet。例如:

>>> from django.db.models import Count
>>> from cvat.apps.engine.models import Project, Task
>>> tsk = Task.objects.annotate(Count('overlap'))
>>> print(tsk)
<TaskQuerySet [<Task: test-3>, <Task: test-task>, <Task: test-2>]>

        上述返回值看似与原QuerySet一致,当查看tsk对象的字段时可以发现,不同的是返回的每个QuerySet条目实际上都增加了一个如下所示的字段,即下图的最后一个字段overlap__count。

>>> print(tsk[0].__dict__)
{'_state': <django.db.models.base.ModelState object at 0x7f99fd7d3370>, 'id': 1, 'project_id': None, 'name': 'test-task', 'mode': 'annotation', 'owner_id': 1, 'assignee_id': None, 'bug_tracker': '', 'created_date': datetime.datetime(2023, 7, 4, 15, 8, 54, 322331, tzinfo=datetime.timezone.utc), 'updated_date': datetime.datetime(2023, 7, 26, 7, 5, 37, 486389, tzinfo=datetime.timezone.utc), 'overlap': 0, 'segment_size': 1, 'status': 'completed', 'data_id': 1, 'dimension': '2d', 'subset': '', 'organization_id': None, 'source_storage_id': 1, 'target_storage_id': 2, 'overlap__count': 1}

        这是聚合操作模型产生的字段,django允许自定义该字段,即:

tsk = Task.objects.annotate(defined_name=Count('overlap'))

        同时也支持组合多个聚合,但需要注意添加参数distinct=True以防止join

tsk = Task.objects.annotate(Count('overlap',distinct=True), Count('segment_size',distinct=True))

        此外,django还支持join链式聚合查询,也就是说能够聚合查询模型的关联模型的值。例如,对于主表project与子表task可以实现双向查询:

Project.objects.annotate(Avg('task__overlap'))
Project.objects.aggregate(Avg('task__overlap'))
# 当然这里的project__id是没有实际意义的,举例罢了
Task.objects.annotate(Avg('Project__id'))
Task.objects.aggregate(Avg('Project__id'))

        至于其他的,annotate与order_by(),values(),Filter的连用的方法暂时未遇到且不聊,其使用可参考:Django基础(24): aggregate和annotate方法使用详解与示例 - 知乎 (zhihu.com)

参考文献 

1、Django聚合数据 - floodwater - 博客园 (cnblogs.com)

2、Django基础(24): aggregate和annotate方法使用详解与示例 - 知乎

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Felier.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值