• 【Django】聚合查询——聚合和其他 QuerySet 子句(filter() 、 exclude()、order_by()、values())


    当使用复杂的ORM方式查询时,如果有困惑,使用 str(queryset.query) 查看对应生成的SQL语句

    聚合和其他 QuerySet 子句

    filter() 和 exclude()

    聚合结果也可以使用过滤。任何应用于普通模型字段的 filter() (或 exclude())对聚合的对象也有约束效果。

    当与 annotate() 子句一起使用时,过滤器的效果是限制用来计算“注释”( annotate() 子句会给queryset中的对象增加一个属性和值,称之为“注释”)的对象。

    例如,以下查询生成所有标题以 “Django” 开头的书籍的带注释列表:

    >>> from django.db.models import Avg, Count
    >>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count("authors"))
    
    • 1
    • 2

    当与 aggregate() 子句一起使用时,过滤器的效果是限制用来计算聚合的对象。

    例如,以下查询生成所有标题以 “Django” 开头的书籍的平均价格:

    >>> Book.objects.filter(name__startswith="Django").aggregate(Avg("price"))
    
    • 1

    过滤注释

    注解过的值也可以使用过滤器。注解的别名可以和任何其他模型字段一样使用 filter()exclude()子句。(因为使用 annotate()生成的也是一个QuerySet,根据链式查询的规则,是可以这样的。)
    例如,要生成一个具有多位作者的书籍列表:

    >>> Book.objects.annotate(num_authors=Count("authors")).filter(num_authors__gt=1)
    
    • 1

    annotate() 和 filter() 子句的顺序

    当开发一个涉及 annotate()filter() 子句的复杂查询时,要特别注意应用于 QuerySet 的子句的顺序。

    当一个 annotate() 子句应用于查询,会根据查询状态来计算注释。这实际上意味着 filter()annotate() 不是可交换的操作。

    filter()用在前面,会先过滤对象,再生成注释。
    annotate()用在前面,会先生成注释,然后过滤对象,在多对多的查询和一对多的反向查询时,会容易观察到差异。例如:

    • 出版者A有两本评分4和5的书。
    • 出版者B有两本评分1和4的书。
    • 出版者C有一本评分1的书。
      以下是一个使用 Count 聚合的示例:
    >>> a, b = Publisher.objects.annotate(avg_rating=Avg("book__rating")).filter(
    ...     book__rating__gt=3.0
    ... )
    >>> a, a.avg_rating
    (<Publisher: A>, 4.5)  # (5+4)/2
    >>> b, b.avg_rating
    (<Publisher: B>, 2.5)  # (1+4)/2
    
    >>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(
    ...     avg_rating=Avg("book__rating")
    ... )
    >>> a, a.avg_rating
    (<Publisher: A>, 4.5)  # (5+4)/2
    >>> b, b.avg_rating
    (<Publisher: B>, 4.0)  # 4/1 (book with rating 1 excluded)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第一个查询请求至少有一本评分3以上的书籍的出版者的书籍平均分。第二个查询只请求评分3以上的作者书籍的平均评分。

    order_by()

    annotate() 子句之后可以用order_by()排序,因为注释相当于查询对象结果的一个属性了。
    例如,要按参与书籍创作的作者数量对书籍的 QuerySet 进行排序:

    >>> Book.objects.annotate(num_authors=Count("authors")).order_by("num_authors")
    
    • 1

    values()

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

    >>> Author.objects.annotate(average_rating=Avg("book__rating"))
    
    • 1

    使用了values("name")

    Author.objects.values("name").annotate(average_rating=Avg("book__rating"))
    
    • 1

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

    annotate() 和 values() 的顺序

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

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

    >>> Author.objects.annotate(average_rating=Avg("book__rating")).values(
    ...     "name", "average_rating"
    ... )
    
    • 1
    • 2
    • 3

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

    和 order_by() 一起用会发生化学反应

    这里需要注意的一点是order_by()会影响分组。

    聚合所生成的注释

    你也可以在生成的注释结果上生成聚合。例如,如果您想计算每本书的平均作者数量,您首先要用作者数量对书籍集进行注释,然后对该作者数量进行聚合:

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

    在空查询集或组上进行聚合操作时,需要格外小心,因为这可能会导致未定义的行为或错误。在执行聚合操作之前,通常应确保查询集或组中包含足够的数据以执行所需的聚合计算。如果查询集或组为空,可以使用条件语句来避免聚合错误或不必要的操作。

    当对空的查询集或分组应用聚合操作时,结果通常默认为其 default 参数,通常是 None。这种行为发生是因为当执行的查询不返回任何行时,聚合函数会返回 NULL。

  • 相关阅读:
    【API 管理】什么是 API 管理,为什么它很重要?
    目标检测YOLO实战应用案例100讲-基于机器视觉的输电线路小目标检测和缺 陷识别(下)
    Mendix发布全球低代码报告,中国软件与低代码发展远超全球
    开发工程师必备————【Day33】Django补充(八)
    springmvc 全局异常处理器配置的三种方式&深入底层源码分析原理
    C++:map和set
    SpringBoot大文件上传实现分片、断点续传
    腾讯云短信申请
    [BigData:Hadoop]:安装部署篇
    java ThreadPoolExecutor怎么设置队列不满也能达到最大线程池大小
  • 原文地址:https://blog.csdn.net/chengyikang20/article/details/136474554