首页 > 其他分享 > django聚合查询与分组查询

django聚合查询与分组查询

时间:2022-11-23 22:12:34浏览次数:58  
标签:__ values django annotate objects 分组 query 查询 book

目录

django聚合查询与分组查询

例题

聚合、分组查询

# 所有书的平均价格
avg_query = Book.objects.all().aggregate(Avg("price"))
print(avg_query)  # {'price__avg': Decimal('93.6888888888888889')}

# 作者jason写的书的最大价格
max_query = Book.objects.filter(authors__name="jason").aggregate(Max("price"))
print(max_query)  # {'price__max': Decimal('151.00')}

# 所有书的价格的最大值和最小值的差值
query = Book.objects.aggregate(
  price_diff=Max('price', output_field=IntegerField()) - Min('price', output_field=IntegerField())
)
print(query)  # {'price_diff': 551}  output_field参数可以指定返回的字段类型

# 以出版社分组,第一个出版社下出版了多少种书
query = Publish.objects.annotate(num_books=Count('book'))
print(query)  # <QuerySet [<Publish: 东方出版社>, <Publish: 南方出版社>, <Publish: 西方出版社>, <Publish: 北方出版社>]>
print(query[0].num_books)  # 3

# 以出版社分组,第一个出版社下出版书的最低价格
query = Publish.objects.annotate(min_price=Min('book__price'))
print(query[0].min_price)  # 11.00

# 只有一个作者的书(生成的新字段是可以当作过滤条件的,甚至可以用双下划线)
query = Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=1)
print(query)  # <QuerySet [<Book: python开发>, <Book: 西游记>]>

# 查询每个作者出的书的总价格
query = Author.objects.annotate(sum_price=Sum('book__price'))
print(query[0], query[0].sum_price)  # jason 195.00

# 以出版社分组,查出评级>3的书的数量和评级<=3的数量
'''
        Count这里的用法,仅仅是在分组查询的时候多加过滤条件,
        这里above_3就是计算书的数量,什么书?评级大于3的书的数量,
        并且必须结合Q一起使用, above_3其实就只是一个参数当作筛选条件,
        如果在使用多个聚合时,并且聚合的字段是来自不同的表,那么Count
        最好加上distinct=True,来确保数据的正确,因为底层使用的是join
        而不是子查询,所以可能会出现重复的数据
'''
above_3 = Count('book', filter=Q(book__rating__gt=3))
below_3 = Count('book', filter=Q(book__rating__lte=3))
# 这里其实就是按出版社分组,查出两个字段,相当于annotate两个连用
query = Publish.objects.annotate(below_3=below_3, above_3=above_3)
print(query, query[0].above_3, query[0].below_3)  # <QuerySet [<Publish: 东方出版社>, <Publish: 南方出版社>, <Publish: 西方出版社>, <Publish: 北方出版社>]> 1 2
# 可以这么写
query = Publish.objects.annotate(
  above_3=Count('book', filter=Q(book__rating__gt=3)),
  below_3=Count('book', filter=Q(book__rating__lte=3))
)
# 也可以按下面的方式写,只是要分成两个步骤
query1 = Publish.objects.filter(book__rating__gt=3).annotate(num_books=Count('book'))
print(query1, query1[0].num_books)  # 每个出版社评级大于3的书的数量
query2 = Publish.objects.filter(book__rating__lte=3).annotate(num_books=Count('book'))
print(query2, query2[0].num_books)  # 每个出版社评级小于等于3的书的数量

# 以出版社分组,查出书的数量,并且按数量排序,取前三名出版社的数据
query = Publish.objects.annotate(num_book=Count('book')).order_by('-num_book')[:3]
print(query, query[0].num_book)  # <QuerySet [<Publish: 东方出版社>, <Publish: 西方出版社>, <Publish: 南方出版社>]> 3

Count( )例子

# 每个 account_type 有多少个客户
Client.objects.create(
...     name='Jean Grey',
...     account_type=Client.REGULAR,
...     registered_on=date.today())
>>> Client.objects.create(
...     name='James Bond',
...     account_type=Client.PLATINUM,
...     registered_on=date.today())
>>> Client.objects.create(
...     name='Jane Porter',
...     account_type=Client.PLATINUM,
...     registered_on=date.today())
>>> # Get counts for each value of account_type
>>> from django.db.models import Count
>>> Client.objects.aggregate(
  			# 多个regular字段及结果,计算account_type=regular的数量
...     regular=Count('pk', filter=Q(account_type=Client.REGULAR)),
  			# 多个gold字段及结果, 计算account_type=gold的数量
...     gold=Count('pk', filter=Q(account_type=Client.GOLD)),
  			# 多个platinum字段及结果, 计算account_type=platinum的数量
...     platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
... )
{'regular': 2, 'gold': 1, 'platinum': 3}

order_by()

注解可以当做基本排序来使用。当你定义了一个 order_by() 子句,你提供的聚合可以引用任何定义为查询中 annotate() 子句的一部分的别名。

比如,通过书籍的作者数量来对书籍的 QuerySet 排序,你可以使用下面的查询:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

values()

通常,注解值会添加到每个对象上,即一个被注解的 QuerySet 将会为初始 QuerySet 的每个对象返回一个结果集。然而,当使用 values() 子句来对结果集进行约束时,生成注解值的方法会稍有不同。不是在原始 QuerySet 中对每个对象添加注解并返回,而是根据定义在 values() 子句中的字段组合先对结果进行分组,再对每个单独的分组进行注解,这个注解值是根据分组中所有的对象计算得到的。

下面是一个关于作者的查询例子,查询每个作者所著书的平均评分:

>>> Author.objects.annotate(average_rating=Avg('book__rating'))

这段代码返回的是数据库中的所有作者及其所著书的平均评分。

但是如果你使用 values() 子句,结果会稍有不同:

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

在这个例子中,作者会按名字分组,所以你只能得到不重名的作者分组的注解值。这意味着如果你有两个作者同名,那么他们原本各自的查询结果将被合并到同一个结果中;两个作者的所有评分都将被计算为一个平均分。

annotate()values() 的顺序

和使用 filter() 一样,作用于某个查询的 annotate()values() 子句的顺序非常重要。如果 values() 子句在 annotate() 之前,就会根据 values() 子句产生的分组来计算注解。

然而如果 annotate() 子句在 values() 之前,就会根据整个查询集生成注解。这种情况下,values() 子句只能限制输出的字段。

举个例子,如果我们颠倒上个例子中 values()annotate() 的顺序:

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

这段代码将为每个作者添加一个唯一注解,但只有作者姓名和 average_rating 注解会返回在输出结果中。

你应该也会注意 average_rating 已经明确包含在返回的值列表中。这是必需的,因为 values()annotate() 子句的顺序。

如果 values() 子句在 annotate() 子句之前,任何注解将自动添加在结果集中。然而,如果 values() 子句应用在 annotate() 子句之后,则需要显式包含聚合列。

分组加聚合

你也可以在注解结果上生成聚合。当你定义 aggregate() 子句时,你提供的聚合可以引用任何定义在查询中 annotate() 子句的别名。

比如,如果你想计算每本书的平均作者数,首先使用作者数注解书籍集合,然后引用注解字段聚合作者数:

>>> from django.db.models import Avg, Count
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}

标签:__,values,django,annotate,objects,分组,query,查询,book
From: https://www.cnblogs.com/zonghan/p/16920323.html

相关文章