【Django】聚合查询

本篇以下面的模型为基础进行讨论,根据查询目标列出示例代码,和示例结果。

python 复制代码
from django.db import models

# 作者
class Author(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

# 出版社
class Publisher(models.Model):
    name = models.CharField(max_length=300)

# 图书
class Book(models.Model):
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    rating = models.FloatField()
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    pubdate = models.DateField()

# 
class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)

count()

查询图书总数

python 复制代码
>>> Book.objects.count()
2452

出版社名为"WordPress"的图书总数

python 复制代码
>>> Book.objects.filter(publisher__name="WordPress").count()
73

aggregate()

aggregate()方法是在整个 QuerySet 上生成聚合值。

所有书的均价,如果没有书则返回默认值0

python 复制代码
>>> from django.db.models import Avg
>>> Book.objects.aggregate(Avg("price", default=0))
{'price__avg': 34.35}

所有书中最贵的价格,如果没有书则返回默认值0

python 复制代码
>>> from django.db.models import Max
>>> Book.objects.aggregate(Max("price", default=0))
{'price__max': Decimal('81.20')}

aggregate() 是一个 QuerySet 的终端子句,当调用时,它返回一个字典。名称是聚合值的标识符;值是计算得到的聚合值。名称是从字段名称和聚合函数自动生成的。如果您想手动指定聚合值的名称,可以在指定聚合子句时提供该名称:

python 复制代码
>>> Book.objects.aggregate(average_price=Avg("price"))
{'average_price': 34.35}

如果您想生成多个聚合值,可以向 aggregate() 子句添加另一个参数。因此,如果我们还想知道所有书的最高价和最低价,可以发出以下查询:

python 复制代码
>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

书中最贵价格和平均价格的差值

python 复制代码
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
...     price_diff=Max("price", output_field=FloatField()) - Avg("price")
... )
{'price_diff': 46.85}

annotate()

使用 annotate() 子句可以生成每一个对象的汇总。当指定 annotate() 子句,QuerySet 中的每一个对象将对指定值进行汇总。

aggregate() 相同点,注释的名称是从聚合函数的名称和被聚合字段的名称自动派生的。您可以通过在指定注释时提供别名来覆盖这个默认名称。

aggregate() 不同的是,annotate() 不是终端子句。annotate() 子句的输出就是 QuerySet;这个 QuerySet 可以像其他 QuerySet 一样进行操作,包括 filter(), order_by() ,甚至可以对 annotate() 进行额外调用。

查询每个出版社的图书总数,并添加到"num_books"属性值中

python 复制代码
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count("book"))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73

查询每个出版社评分大于5的书数量和小于等于5的书的数量,并添加到"above_5"和"below_5"的属性值中

python 复制代码
>>> from django.db.models import Q
>>> above_5 = Count("book", filter=Q(book__rating__gt=5))
>>> below_5 = Count("book", filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12

找出书籍数量排名前5的出版社,并添加到"num_books"属性值中

python 复制代码
>>> pubs = Publisher.objects.annotate(num_books=Count("book")).order_by("-num_books")[:5]
>>> pubs[0].num_books
1323

还有个地方需要注意:使用 annotate() 组合多个聚合将产生错误的结果,因为它使用连接(joins)而不是子查询,下面是一个错误示例:

python 复制代码
>>> book = Book.objects.first()
>>> book.authors.count()
2
>>> book.store_set.count()
3
>>> q = Book.objects.annotate(Count("authors"), Count("store"))
>>> q[0].authors__count
6
>>> q[0].store__count
6

对大部分聚合来说,没办法避免这个问题,但是,Count 聚合可以使用 distinct 参数来避免:

python 复制代码
>>> q = Book.objects.annotate(
...     Count("authors", distinct=True), Count("store", distinct=True)
... )
>>> q[0].authors__count
2
>>> q[0].store__count
3
相关推荐
薛晓刚1 小时前
当MySQL的int不够用了
数据库
SelectDB技术团队2 小时前
Apache Doris 在菜鸟的大规模湖仓业务场景落地实践
数据库·数据仓库·数据分析·apache doris·菜鸟技术
再吃一根胡萝卜2 小时前
使用 squashmigrations 命令优化 Django 迁移文件
python·django
星空下的曙光2 小时前
mysql 命令语法操作篇 数据库约束有哪些 怎么使用
数据库·mysql
小楓12012 小时前
MySQL數據庫開發教學(一) 基本架構
数据库·后端·mysql
染落林间色2 小时前
达梦数据库-实时主备集群部署详解(附图文)手工搭建一主一备数据守护集群DW
数据库·sql
颜颜yan_2 小时前
企业级时序数据库选型指南:从传统架构向智能时序数据管理的转型之路
数据库·架构·时序数据库
lichenyang4532 小时前
管理项目服务器连接数据库
数据库·后端
沙振宇3 小时前
【数据库】通过‌phpMyAdmin‌管理Mysql数据
数据库·mysql
杨云龙UP3 小时前
CentOS Linux 7 (Core)上部署Oracle 11g、19C RAC详细图文教程
数据库·oracle