aggregate和annotate方法使用详解与示例
aggregate和annotate方法的使用场景
Django的aggregate和annotate方法属于高级查询方法,主要用于组合查询。当我们需要对查询集(queryset)的某些字段进行计算或进行先分组再计算或排序, 我们就需要使用aggregate和annotate方法了。
假如我们有如下一个模型,其中Student与Hobby(爱好)是多对多的关系。我们想要知道所有学生的平均年龄,我们常规做法一般是利用for循环从数据库中把符合查询条件的student对象一个一个取出,把他们年龄相加,然后再除以总人数。当人数非常多而我们又只需要平均年龄这条信息时,把所有符合查询条件的学生对象都载入内存后再进行计算是非常浪费资源的,效率也非常低。一个更好的方法是在数据库层面提取查询数据时就直接返回我们所需要的信息。因为这个查询涉及到对整个queryset的age字段进行统计计算,此时django的聚合函数方法aggregate就可以帮我们大大提升查询效率了[见后文]。
class Student(models.Model): name = models.CharField(max_length=20)
age = models.IntegerField()
hobbies = models.ManyToManyField(Hobby) class Hobby(models.Model):
name = models.CharField(max_length=20)
另一个例子是统计最受学生欢迎的5个爱好,常规做法是先将所有hobby对象提取出来,载入内存。然后利用for循环统计每组爱好对应的学生人数,再构建一个新的查询集,按每组人数从大到小进行排序。这个查询需要根据hobby先进行分组,再统计每个爱好组里学生的数量,然后进行排序。对于这个复杂查询, django的annotate方法一句话就可以解决问题。
aggregate()方法详解
aggregate的中文意思是聚合, 源于SQL的聚合函数。Django的aggregate()方法作用是对一组值(比如queryset的某个字段)进行统计计算,并以字典(Dict)格式返回统计计算结果。django的aggregate方法支持的聚合操作有AVG / COUNT / MAX / MIN /SUM 等。
我们现在来看下几组实际使用案例。使用前别忘了import Avg, Max, Min或者Sum方法哦
from django.db.models import Avg, Max, Min
# 计算学生平均年龄, 返回字典。age和avg间是双下划线哦
Student.objects.all().aggregate(Avg('age')) { 'age__avg': 12 }
# 学生平均年龄,返回字典。all()不是必须的。
Student.objects.aggregate(Avg('age')) { 'age__avg: 12' }
# 计算学生总年龄, 返回字典。
Student.objects.aggregate(Sum('age')) { 'age__sum': 144 }
# 学生平均年龄, 设置字典的key
Student.objects.aggregate(average_age = Avg('age')) { 'average_age': 12 }
# 学生最大年龄,返回字典
Student.objects.aggregate(Max('age')) { 'age__max': 12 }
# 同时获取学生年龄均值, 最大值和最小值, 返回字典
Student.objects.aggregate(Avg('age‘), Max('age‘), Min('age‘)) { 'age__avg': 12, 'age__max': 18, 'age__min': 6, }
# 根据Hobby反查学生最大年龄。查询字段student和age间有双下划线哦。
Hobby.objects.aggregate(Max('student__age')) { 'student__age__max': 12 }
aggregate方法返回Dict类型数据和django的内容对象(context object)是一样的。可以很轻松地将结果传递给模板, 在模板中显示。
annotate()方法详解
annotate的中文意思是注释,小编我觉得是非常地词不达意,一个更好的理解是分组(Group By)。如果你想要对数据集先进行分组然后再进行某些聚合操作或排序时,需要使用annotate方法来实现。与aggregate方法不同的是,annotate方法返回结果的不仅仅是含有统计结果的一个字典,而是包含有新增统计字段的查询集(queryset).
我们接下来也看下几个实际使用案例。
# 按学生分组,统计每个学生的爱好数量
Student.objects.annotate(Count('hobbies'))
返回的结果依然是Student查询集,只不过多了hobbies__count这个字段。如果你不喜欢这个默认名字,你当然可以对这个字段进行自定义从而使它变得更直观。
# 按学生分组,统计每个学生爱好数量,并自定义字段名
Student.objects.annotate(hobby_count_by_student=Count('hobbies'))
# 按爱好分组,再统计每组学生数量。
Hobby.objects.annotate(Count('student'))
# 按爱好分组,再统计每组学生最大年龄。
Hobby.objects.annotate(Max('student__age'))
Annotate方法与Filter方法联用
有时我们需要先对数据集先筛选再分组,有时我们还需要先分组再对查询集进行筛选。根据需求不同,我们可以合理地联用annotate方法和filter方法。注意: annotate和filter方法联用时使用顺序很重要。
# 先按爱好分组,再统计每组学生数量, 然后筛选出学生数量大于1的爱好。
Hobby.objects.annotate(student_num=Count('student')).filter(student_num__gt=1)
# 先按爱好分组,筛选出以'd'开头的爱好,再统计每组学生数量。
Hobby.objects.filter(name__startswith="d").annotate(student_num=Count('student‘))
Annotate与order_by()联用
我们同样可以使用order_by方法对annotate方法返回的数据集进行排序。
# 先按爱好分组,再统计每组学生数量, 然后按每组学生数量大小对爱好排序。
Hobby.objects.annotate(student_num=Count('student‘)).order_by('student_num')
# 统计最受学生欢迎的5个爱好。
Hobby.objects.annotate(student_num=Count('student‘)).order_by('-student_num')[:5]
Annotate与values()联用
我们在前例中按学生对象进行分组,我们同样可以按学生姓名name来进行分组。唯一区别是本例中,如果两个学生具有相同名字,那么他们的爱好数量将叠加。
# 按学生名字分组,统计每个学生的爱好数量。
Student.objects.values('name').annotate(Count('hobbies'))
你还可以使用values方法从annotate返回的数据集里提取你所需要的字段,如下所示:
# 按学生名字分组,统计每个学生的爱好数量。
Student.objects.annotate(hobby_count=Count('hobbies')).values('name', 'hobby_count')
小结
Django的aggregate和annotate方法属于高级查询方法,主要用于组合查询,可以大大提升数据库查询效率。当需要对查询集(queryset)的某些字段进行聚合操作时(比如Sum, Avg, Max),请使用aggregate方法。如果想要对数据集先进行分组(Group By)然后再进行某些聚合操作或排序时,请使用annotate方法。
aggregate和annotate方法使用详解与示例的更多相关文章
- CentOS 6.3下Samba服务器的安装与配置方法(图文详解)
这篇文章主要介绍了CentOS 6.3下Samba服务器的安装与配置方法(图文详解),需要的朋友可以参考下 一.简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件, ...
- jQuery操纵DOM元素属性 attr()和removeAtrr()方法使用详解
jQuery操纵DOM元素属性 attr()和removeAtrr()方法使用详解 jQuery中操纵元素属性的方法: attr(): 读或者写匹配元素的属性值. removeAttr(): 从匹配的 ...
- php课程---Windows.open()方法参数详解
Window.open()方法参数详解 1, 最基本的弹出窗口代码 window.open('page.html'); 2, 经过设置后的弹出窗口 window.open('page.html ...
- Window.open()方法参数详解
Window.open()方法参数详解 1, 最基本的弹出窗口代码 window.open('page.html'); 2, 经过设置后的弹出窗口 window.open('page.html ...
- 集合类 Contains 方法 深入详解 与接口的实例
.Net 相等性:集合类 Contains 方法 深入详解 http://www.cnblogs.com/ldp615/archive/2009/09/05/1560791.html 1.接口的概念及 ...
- [转]js中几种实用的跨域方法原理详解
转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...
- C#虚方法virtual详解
转: http://www.cnblogs.com/jason_yjau/archive/2009/08/25/1553949.html C#虚方法virtual详解 在C++.Java等众多OOP语 ...
- JQuery动画animate的stop方法使用详解
JQuery动画animate的stop方法使用详解 animate语法: 复制代码 代码如下: $(selector).animate(styles,speed,easing,callback) 复 ...
- Linux NFS服务器的安装与配置方法(图文详解)
这篇文章主要介绍了Linux NFS服务器的安装与配置方法(图文详解),需要的朋友可以参考下(http://xb.xcjl0834.com) 一.NFS服务简介 NFS 是Network File S ...
随机推荐
- 在gnome3.x中添加eclipse菜单
ubuntu-gnome 16.04采用gnome3,界面和gnome2有较大区别.对于自己手动解压安装,而非用包管理器安装的程序,需要手工建立应用程序的启动快捷方式.例如,对于eclipse程序,采 ...
- 【OpenJ_Bailian - 4152 】最佳加法表达式(动态规划)
最佳加法表达式 Descriptions: 给定n个1到9的数字,要求在数字之间摆放m个加号(加号两边必须有数字),使得所得到的加法表达式的值最小,并输出该值.例如,在1234中摆放1个加号,最好的摆 ...
- Ubuntu18安装sublime 3
转自 https://blog.csdn.net/qq_41590417/article/details/80461075 wget -qO - https://download.sublimetex ...
- HDU 5558 后缀数组
思路: 这是一个错误的思路, 因为数据水才过= = 首先求出来后缀数组 把rank插到set里 每回差i两边离i近的rank值,更新 如果LCP相同,暴力左(右)继续更新sa的最小值 //By Sir ...
- 【洛谷5398】[Ynoi2018]GOSICK(二次离线莫队)
题目: 洛谷 5398 当我刚学莫队的时候,他们告诉我莫队能解决几乎所有区间问题: 现在,当我发现一个区间问题似乎难以用我所了解的莫队解决的时候,他们就把这题的正解叫做 XXX 莫队.--题记 (以上 ...
- 题解报告:hdu 1408 盐水的故事
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1408 Problem Description 挂盐水的时候,如果滴起来有规律,先是滴一滴,停一下:然后 ...
- DEV—【GridControl添加按钮列】
效果图 打开GridControl的Run Designer在左侧栏中找到: 添加一个ButtonEdit: 更改属性中的值:Caption为按钮上显示的Text:Kind为按钮的类型: 然后拖到最后 ...
- Ref 和 Out 区别(演练代码)
一.代码 今天就总结Ref和Out 的总结,这东西,也是经常面试过程中,笔试经常考的,比如:请简述Ref和Out 的区别,或者通过一段代码让你计算这过程的结果.... Out代码实例::: stati ...
- [Luogu2901][USACO08MAR]牛慢跑Cow Jogging Astar K短路
题目链接:https://daniu.luogu.org/problem/show?pid=2901 Astar的方程$f(n)=g(n)+h(n)$,在这道题中我们可以反向最短路处理出$h(n)$的 ...
- WPF 实时绘图的逻辑
实时绘图实际上是两个线程.外部线程直接用thread,只有到绘图那个逻辑才用绘图控件的mycanvas2.Dispatcher.Invoke. 或者说,INVOKE并不是开线程,只是一个绘图的委托而已 ...