【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
相关推荐
roman_日积跬步-终至千里1 天前
【LangGraph4j】LangGraph4j 核心概念与图编排原理
java·服务器·数据库
汇智信科1 天前
打破信息孤岛,重构企业效率:汇智信科企业信息系统一体化运营平台
数据库·重构
野犬寒鸦1 天前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
WHD3061 天前
苏州数据库(SQL Oracle)文件损坏修复
hadoop·sql·sqlite·flume·memcached
晚霞的不甘1 天前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
市场部需要一个软件开发岗位1 天前
JAVA开发常见安全问题:纵向越权
java·数据库·安全
海奥华21 天前
mysql索引
数据库·mysql
2601_949593651 天前
深入解析CANN-acl应用层接口:构建高效的AI应用开发框架
数据库·人工智能
javachen__1 天前
mysql新老项目版本选择
数据库·mysql
Dxy12393102161 天前
MySQL如何高效查询表数据量:从基础到进阶的优化指南
数据库·mysql