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')}
Decimal这个值提取出来可以用 to_eng_string() 或者 to_interger()等方法

xxx = {'price__sum': Decimal('320.00')};
print xxx['price__sum'].to_eng_string()

分组查询

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')}]

annotate(聚合函数)
values在前,表示group by字段  #msyql中group by只能查询,当前字段,以及聚合函数的值(不设置严格模式是可以取出来所有的值)
        #注意orm里面可以查询分组的表中的所有的字段的值????
values在后,相当于取字段
filter在前,表示where条件
filter在后,表示having 分组后的筛选条件

##注意
1、单表  只能取分组字段跟聚合函数的字段。
2、多表  可以取分组表中的所有的字段,跟聚合函数的字段。

例句:
1、统计每一个本书作者个数
res=models.Book.objects.annotate(book_count=Count('author__id')).values('name','book_count')

2、统计不止一个作者的图书
res2=models.Books.objects.values('pk').annotate(book_count=Count('author__id')).filter(boo_count__gt=1).values('name','book_count')

3、统计价格大于10元,作者个数个数大于1的图书
res3=models.Books.objects.values('pk').filter('price__gt'=10).annotate(author_count=Count('authors__id')).filter(author_count__gt=1).values('name',author_count)

跨表之组合查询

# 跨表分组查询模板
# 每一个后表模型.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")
posted @ 2021-08-23 07:10  零哭谷  阅读(814)  评论(0编辑  收藏  举报