Django ORM聚合查询,分组查询

聚合查询方法名aggregate(),返回的是一个字典,不是queryset,而annotate()返回的是一个queryset,

2个方法区别是aggregate是对一组值进行计算,而annotate更多的是一组值进行group by后再进行计算.

聚合方法需要搭配具体计算的方法一起使用,需要导入avg max min count from django.db.models import Count,Avg,Max,Min

计算书本的平均价格,返回值的key值为 `字段__avg`,

book = models.Book.objects.all().aggregate(Avg("price")) #{'price__avg': Decimal('31.593333')}

也可以自定义key值

book2 = models.Book.objects.all().aggregate(avg_price =Avg("price")) #{'avg_price': Decimal('31.593333')}

分组查询

ORM中,book.Object.all() 等于sql语句的select * from book,而book.Object.all().values("publish")等于sql语句的select publish from book,
ORM中的values有2种意思,一个是只提取publish字段显示,如果Book.objects.values("publish_id").annotate 后面跟了聚合函数,也会按照该字段进行分组.
所以Book.objects.all().annotate 是一句没有意义的聚合命令,因为all()里面包含了id,每条记录都是不同的,因此无法产生按照特定字段进行聚合.

  比如emp表,需要统计各个部门的人数
    emp:
    id  name    age dep
    1   alex        18  销售部
    2   blex        38  销售部
    3   clex        28  市场部
    4   dlex        48  客服部
    
    sql语句
    select Count(id) from emp group by dep; 通过对dep字段分组,再对每组的id个数进行统计
    单表中的分组查询
    orm中分组是用annotate(),返回值是一个query set 
    使用语法是前面先用value("dep").annotate(Avg("age"))
    value("dep") 等于明确按dep分组,语法等同于group by dep
    例子:
    avg_age = models.Book.objects.values("publish_id").annotate(zidingyi =Avg("price"))  #key值和aggregate一样可以自定义
    # QuerySet[{'publish_id': 1, 'zidingyi': Decimal('38.275000')}, {'publish_id': 2, 'zidingyi': Decimal('18.230000')}]

跨表之组合查询

# 跨表分组查询模板
# 每一个后表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型中的所有字段或者需要统计的字段")
# 每一个后表模型.objects.annotate(聚合函数(关联表__统计字段)).values("表模型中的所有字段或者需要统计的字段")
# 每一个后表模型.objects.all().annotate(聚合函数(关联表__统计字段)).values("表模型中的所有字段或者需要统计的字段")
上面第二个和第三个效果等同,因为values("pk")只是选择了pk这个字段,同时带有按这个字段group by的效果
但是我们用all()或者省略不写,ORM的语法意思是按照这个对象的所有字段进行group by的意思.

例1:查询出版社的名称以及出版的书本个数
sql语句示例

select my_book_publish.name,Count(my_book_book.title) from my_book_book inner join my_book_publish on my_book_book.publish_id = my_book_publish.pid group by my_book_publish.pid;

ORM语句示例
以publish为基表有三种写法,推荐用第二种,因为pid是唯一的,而有时候如果name会有重复,比如name为人名的时候

    pub = models.Publish.objects.values("pid").annotate(c =Count("book__title"))
    #< QuerySet[{'pid': 1, 'c': 3}, {'pid': 2, 'c': 2}, {'pid': 3, 'c': 1}] >
    # group by pid,然后annotate通过反向查询 表名__字段 对该字段进行聚合计算,但是结果只显示出版社的pid
    pub = models.Publish.objects.values("pid").annotate(c=Count("book__title")).values("name","c")
    #<QuerySet [{'name': '南京出版社', 'c': 3}, {'name': '北京出版社', 'c': 2}, {'name': '浙江出版社', 'c': 1}]>
    # 因为前面语句已经把2张表拼成了一张表,里面除了出版社和book的字段,c 这个计算结果字段也会包含在内,所以我们要显示出版社name和c可以再加个values进行展示
    pub = models.Publish.objects.values("name").annotate(c=Count("book__title"))
    # group by name,然后annotate通过反向查询 表名__字段 对该字段进行聚合计算
    # <QuerySet [{'name': '南京出版社', 'c': 3}, {'name': '北京出版社', 'c': 2}, {'name': '浙江出版社', 'c': 1}]>

例2:查询每一个作者的名字以及他们出版过书籍的最高价格

    sql语句:  select name,MAX(price) from my_book_book inner join my_book_book_author on my_book_book.id = my_book_book_author.book_id inner join my_book_author on my_book_book_author.author_id = my_book_author.aid group by aid
    
    pub = models.Author.objects.values("pk").annotate(c = Max("book__price")).values("name","c")
    #models.Author.objects.values("pk").annotate(c = Max("book__price"))等于是把author ,book author_book三张表join成了一张,后面可以选择要显示的内容
    #<QuerySet [{'name': 'blex', 'c': Decimal('28.23')}, {'name': 'clex', 'c': None}, {'name': 'alex', 'c': Decimal('28.23')}]>

例3: 查询每一个书籍的名称和对应的作者个数

  sql语句:
    #select title,count(aid) as "作者个数" from my_book_book inner join my_book_book_author  on my_book_book.id = book_id inner join my_book_author on my_book_book_author.author_id = my_book_author.aid group by book_id;

    pub = models.Book.objects.values("pk").annotate(c = Count("author__aid")).values("title","c")
    #Count("author__aid")这里的author是字段不是表名,因为book下本来就有author字段,正向查找按字段
    #<QuerySet [{'title': 'python', 'c': 2}, {'title': 'java', 'c': 2}, {'title': 'c++', 'c': 0}, {'title': 'go', 'c': 0}, {'title': 'c#', 'c': 0}, {'title': 'php', 'c': 0}]>
    #我们也可以通过book查其他表下的字段,语法: 字段(表名)__字段
    pub=models.Book.objects.values("pk").annotate(c=Count("author__name")).filter(c__gt=1).values("title","publish__name")
原文地址:https://www.cnblogs.com/Young-shi/p/15174328.html