django 版本 3.2
python 3.6.8

一、聚合函数

常见的五个聚合函数:

  • Avg (Average) : 平均值
  • Max (Maximum) : 最大值
  • Min (Minimum) : 最小值
  • Sum (Summary) : 求和
  • Count : 个数
    导入语句:
from django.db.models import Avg, Max, Min, Sum, Count, Q, F
# Q查询和F查询也可以一起导入,一般会经常频繁使用

以上五个聚合函数都可以在 aggregate 和 annotate 中使用。

二、聚合查询 aggregate()

2.1 aggregate() 终止子句

aggregate()是QuerySet 的一个终止子句,也就是说在写QuerySet 查询语句时aggregate()后面不能再有其他查询语句,因为aggregate()会返回一个键值对的字典,不再是QuerySet 对象。

2.2 使用方法,可自定义变量key

使用方法如下:

from django.db.models import Avg, Max, Min, Sum, Count, Q, F

queryset =  models.MyModel.objects.aggregate(Avg('price')) 
print(queryset)  
# 打印结果: {'price__avg': Decimal('34.204000')} 系统会字典生产一个由“变量名_方法名”组成的字典key,这样并不方便,我们可以自定义key,如下:
queryset =  models.MyModel.objects.aggregate(my_key=Avg('price')) 
print(queryset)  
# 打印结果: {'my_key': Decimal('34.204000')} 

2.3 多个聚合函数一起查询

使用方法如下:

from django.db.models import Avg, Max, Min, Sum, Count, Q, F

queryset =  models.MyModel.objects.aggregate(Avg('price'),Max('price'),Min('price'),Sum('price')) 

三、分组查询 annotate

上面的聚合查询其实经常需要先分组再查询,那么就会用到 annotate

3.1 annotate()不是终止子句但等于终止子句

annotate查询的结果不是键值对的字典,但是一个queryset列表对象,而列表中的元素是字典格式,可以向列表和字典一样取值,因此annotate的后面可以加其他查询语句,例如annotate(Avg(‘price’)) .values("***),但是这么写结果是annotate查询失效,比如:

queryset =  models.MyModel.objects.annotate(Avg('price')) .values("***)

虽然这么写不会报错,但是这么写之后前面的annotate会失去作用!!! 所以网上很多帖子说values() 放前面是什么作用,放后面是什么作用,其实不然,放后面就失去了使用 annotate的意义了。而annotate的前面必须使用 .values().order_by() 先制定按哪个字段分组。
总结,annotate虽然返回的queryset对象,但是后面增加其他查询语句会导致annotate查询失效,因此说annotate()不是终止子句但是等于终止句子。(如果说法有误,欢迎试验后回复订正)

3.2 使用方法,queryset.values(‘**’).order_by() .annotate()

先声明一下django版本3.2; python版本3.6.9 下使用annotate必须按上面的固定写法,少一个都不行,可能不会报错,但是查询不到想要的结果。(可能之前的版本不需要如此,仅供思路参考)
使用方法如下:

from django.db.models import Avg, Max, Min, Sum, Count, Q, F

# 写法一
queryset =  models.MyModel.objects.values('price').order_by().annotate(Count('price')) 
# 写法二
queryset =  models.MyModel.objects.values('price').order_by('price').annotate(Count('price')) 

从方法一和方法二看出,order_by() 中写不写参数都可以,推荐方法一省略order_by()中的参数即可,原因请继续往下看:3.3

3.3 多字段分组 queryset.values(‘price’,‘name’).order_by() .annotate()

写法参考上面两种(但是推荐第一种)

from django.db.models import Avg, Max, Min, Sum, Count, Q, F

# 写法一
queryset =  models.MyModel.objects.values('price''name').order_by().annotate(Count('price')) 
# 写法二
queryset =  models.MyModel.objects.values('price''name').order_by('price').annotate(Count('price')) 

上面两种写法,本人开始也认为是按order_by()种的参数排序分组,其实不然。经过本人实验,order_by()中不管写什么都会去拿values()中的所有字段去分组,所以说values()的作用就是指定分组的字段,order_by() 不需要写任何参数,因为写了也不起作用。

3.4 查询结果字典

查询结果的键值对会包含values()中的参数和annotate()中聚合函数查询结果的key,例如:

from django.db.models import Avg, Max, Min, Sum, Count, Q, F

# annotate 中不指定自定义key 
queryset =  models.MyModel.objects.values('price''name').order_by().annotate(Count('price')) 
print(queryset )
# 打印结果:<QuerySet [{'price': '23', 'name': '张山', 'price_count': '23',}]>

#  annotate 中指定自定义key 
queryset =  models.MyModel.objects.values('price''name').order_by().annotate(my_count = Count('price')) 
print(queryset )
# 打印结果:<QuerySet [{'price': '23', 'name': '张山', 'my_count ': '23',}]>

3.5 关于annotate中多次使用聚合函数Count时的参数distinct=True

之前由帖子提到,一条查询语句annotate中多次Count查询要使用distinct=True 其实已经没有用了,因为前面已经使用了values().order_by()进行了精确分组,所以在django版本3.2; python版本3.6.9 之中甚至之后都没有用了,本人也亲自试验了一下,得到的queryset对象不是字典对象也拿不到想要查询的数据。

四、annotate 于 aggregate的区别

1、终止子句
aggregate 是终止子句,后面加任何查询语句都会报错。
annotate 不是终止子句,后面加任何查询虽然不会报错,但会导致annotate 查询失效。
2、
简单的说 annotate 是先分组再聚合查询,aggregate 是仅仅聚合查询,即便在 aggregate 前面加上 annotate 前面相同的分组条件 values(‘**’).order_by() 也不会分组。

from django.db.models import Avg, Max, Min, Sum, Count, Q, F

# annotate 分组查询
queryset =  models.MyModel.objects.values('price''name').order_by().annotate (my_count = Count('price')) 
print(queryset )
# 打印结果:<QuerySet [{'price': '23', 'name': '张山', 'my_count ': '23',}]>  是个 QuerySet 列表对象,列表中的元素是字典格式,如果有多个分组,对象列表中会有多个字典元素


#  aggregate 聚合查询
queryset =  models.MyModel.objects.values('price''name').order_by().aggregate(my_count = Count('price')) 
print(queryset )
# 打印结果:{'price': '23', 'name': '张山', 'my_count ': '23'},直接就是个字典,即便有多个分组,也是相同的结果,aggregate只会统计相全部,不会按分组查询。因此上面的写法,等同于:
queryset =  models.MyModel.objects.aggregate(my_count = Count('price')) 
11-21 16:40