表关系如下:
author
+----+--------+-----+------------+
| id | name | age | email |
+----+--------+-----+------------+
| 1 | 曹雪芹 | 35 | cxq@qq.com |
| 2 | 吴承恩 | 28 | wce@qq.com |
| 3 | 罗贯中 | 36 | lgz@qq.com |
| 4 | 施耐庵 | 46 | sna@qq.com |
+----+--------+-----+------------+
book
+----+----------+-------+-------+--------+-----------+--------------+----------+
| id | name | pages | price | rating | author_id | publisher_id | remark |
+----+----------+-------+-------+--------+-----------+--------------+----------+
| 1 | 三国演义 | 987 | 108 | 4.8 | 3 | 1 | 三国演义 |
| 2 | 水浒传 | 967 | 107 | 4.83 | 4 | 1 | 水浒传 |
| 3 | 西游记 | 1004 | 105 | 4.85 | 2 | 2 | 西游记 |
| 4 | 红楼梦 | 1007 | 109 | 4.9 | 1 | 2 | 红楼梦 |
+----+----------+-------+-------+--------+-----------+--------------+----------+
book_order
+----+-------+---------+----------------------------+
| id | price | book_id | create_time |
+----+-------+---------+----------------------------+
| 1 | 95 | 1 | 2019-06-17 07:10:02.053000 |
| 2 | 85 | 1 | 2020-06-17 07:10:02.053000 |
| 3 | 88 | 1 | 2020-06-17 07:10:02.053000 |
| 4 | 94 | 2 | 2020-06-17 07:10:02.053000 |
| 5 | 93 | 2 | 2020-06-17 07:10:02.053000 |
+----+-------+---------+----------------------------+
平均值-AVG()
使用aggregate和annotate执行AVG():
from django.db.models import Avg
from django.db import connection
#查看所有书的平均价格
result = Book.objects.aggregate(Avg("price"))
print(result)
print(connection.queries)
>>>{'price__avg': 97.25}
>>>SELECT AVG(`book`.`price`) AS `price__avg` FROM `book`
#查看每本书的对应所有订单的平均售价
result = Book.objects.annotate(avg = Avg("bookorder__price"))
print(result)
print(connection.queries)
>>><QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>]>
>>>SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, AVG(`book_order`.`price`) AS `avg` FROM `book` LEFT OUTER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) GROUP BY `book`.`id` ORDER BY NULL LIMIT 21
相同点:
都可以执行聚合函数
不同点:
1、aggregate返回字典,示例中key默认是字段__avg,Book.objects.aggregate(avg = Avg("price"))
可修改key,结果是{'avg': 97.25}
2、annotate返回QuerySet对象,使用Book.objects.annotate(avg = Avg("bookorder__price")).values()
查看结果,在模型对象属性里增加了平均值属性avg
:
<QuerySet [{'publisher_id': 1, 'name': '三国演义', 'avg': 89.33333333333333, 'rating': 4.8, 'author_id': 3, 'pages': 987, 'price': 98.0, 'id': 1},{...},{...}]>
3、从翻译的sql可以看出,aggregate不做分组,annotate使用group by分组。默认会根据分组字段进行排序。
EXPLAIN
SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, AVG(`book_order`.`price`) AS `avg` FROM `book` LEFT OUTER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) GROUP BY `book`.`id` ORDER BY NULL LIMIT 21
>>>Extra字段 Using temporary
//去掉ORDER BY NULL查看执行计划
>>>Using temporary; Using filesort
orm翻译的sql使用了ORDER BY NULL
禁用排序,提升查找效率。
数量-Count()
from django.db.models import Avg
from django.db import connection
#book表中共有多少个出版社
result = Book.objects.aggregate(nums = Count("publisher_id"))
print(result)
print(connection.queries)
>>>{'nums': 4}
>>>SELECT COUNT(`book`.`publisher_id`) AS `nums` FROM `book`
#book表中共有多少不重复的出版社
result = Book.objects.aggregate(nums = Count("publisher_id",distinct=True))
print(result)
print(connection.queries)
>>>{'nums': 2}
>>>SELECT COUNT(DISTINCT `book`.`publisher_id`) AS `nums` FROM `book`
#统计每本书的销量
books = Book.objects.annotate(nums = Count("bookorder__id")) //bookorder_id可以简写成bookorder
print(books.values())
print(connection.queries)
>>><QuerySet [{'pages': 987, 'publisher_id': 1, 'price': 98.0, 'id': 1, 'name': '三国演义', 'author_id': 3, 'rating': 4.8, 'nums': 3},{...},{...}]>
>>>SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, COUNT(`book_order`.`id`) AS `nums` FROM `book` LEFT OUTER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) GROUP BY `book`.`id` ORDER BY NULL LIMIT 21
最大最小值-Max()、Min()
aggregate()和annotate()可以加多个聚合函数
#author年龄最大值和最小值
result = Author.objects.aggregate(max = Max("age"),min = Min("age"))
print(result)
print(connection.queries)
>>>{'max': 46, 'min': 28}
>>>SELECT MAX(`author`.`age`) AS `max`, MIN(`author`.`age`) AS `min` FROM `author`
#获取每本书售卖时的最高价和最低价
result = Book.objects.annotate(max = Max("bookorder__price"),min = Min("bookorder__price"))
print(result.values())
print(connection.queries)
>>><QuerySet [{'publisher_id': 1, 'min': 85.0, 'max': 95.0, 'author_id': 3, 'name': '三国演义', 'id': 1, 'rating': 4.8, 'pages': 987, 'price': 98.0},{...},{...}]>
>>>SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, MIN(`book_order`.`price`) AS `min`, MAX(`book_order`.`price`) AS `max` FROM `book` LEFT OUTER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) GROUP BY `book`.`id` ORDER BY NULL LIMIT 21
求和-sum()
aggregate:
#2020年所有图书销售总额
result = BookOrder.objects.filter(create_time__year=2020).aggregate(total = Sum("price"))
print(result)
print(connection.queries)
>>>{'total': 360.0}
>>>SELECT SUM(`book_order`.`price`) AS `total` FROM `book_order` WHERE `book_order`.`create_time` BETWEEN '2019-12-31 16:00:00' AND '2020-12-31 15:59:59.999999'
因为setting里面设置了时区是Asia/Shanghai,migrations时数据库里面保存的其实是UTC时间,所以翻译结果是计算后的utc时间BETWEEN '2019-12-31 16:00:00' AND '2020-12-31 15:59:59.999999
,如果要得到和数据库匹配的utc时间,可以修改setting时区为utc
TIME_ZONE = 'UTC'
USE_TZ = True
修改后sql翻译结果:BETWEEN '2020-01-01 00:00:00' AND '2020-12-31 23:59:59.999999'
annotate:
#2020年每本书的销售总额
result = Book.objects.filter(bookorder__create_time__year=2020).annotate(total=Sum("price"))
>>><QuerySet [{'name': '三国演义', 'price': 98.0, 'id': 1, 'author_id': 3, 'total': 196.0, 'publisher_id': 1, 'rating': 4.8, 'pages': 987}, {...},{...}]>
>>>SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, SUM(`book`.`price`) AS `total` FROM `book` INNER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) WHERE `book_order`.`create_time` BETWEEN '2020-01-01 00:00:00' AND '2020-12-31 23:59:59.999999' GROUP BY `book`.`id` ORDER BY NULL LIMIT 21
网友评论