使用Django Debug Toolbar发现统计不同年月之间发布的文章数量这句sql语句非常耗时,需要将近2100ms,于是决定优化其sql。从代码可以知道,老版代码提取article表所有的数据,在数据量过小时,sql语句速度不是太大问题。一旦数据量过大,性能将极速下降,因为用的很多不必要的字段。
源代码:
articles = Article.objects.all() year_month = set() # 设置集合,无重复元素 for a in articles: year_month.add((a.pub_date.year, a.pub_date.month)) # 把每篇文章的年、月以元组形式添加到集合中 counter = {}.fromkeys(year_month, 0) # 以元组作为key,初始化字典 for a in articles: counter[(a.pub_date.year, a.pub_date.month)] += 1 # 按年月统计文章数目 year_month_number = [] # 初始化列表 for key in counter: year_month_number.append([key[0], key[1], counter[key]]) # 把字典转化为(年,月,数目)元组为元素的列表 year_month_number.sort(reverse=True) # 排序
优化后代码:
利用MysqL中ExtractYear,ExtractMonth
from django.db.models.functions import ExtractYear, ExtractMonth from django.db.models import Count count_dict = Article.objects.annotate(year=ExtractYear('pub_date'), month=ExtractMonth('pub_date')) \ .values('year', 'month').order_by('year', 'month').annotate(count=Count('id')) """ count_dict 数据样例 <QuerySet [{'year': 2018, 'month': 7, 'count': 3}, {'year': 2019, 'month': 5, 'count': 7}, 'month': 6, 'count': 161}]> """ year_month_number = [] # 初始化列表 for key in count_dict: year_month_number.append([key['year'], key['month'], key['count']]) # 把字典转化为(年,月,数目)元组为元素的列表 year_month_number.sort(reverse=True) # 排序
sql语句:
SELECT EXTRACT(MONTH FROM `article`.`pub_date`) AS `month`, EXTRACT(YEAR FROM `article`.`pub_date`) AS `year`, COUNT(`article`.`id`) AS `count` FROM `article` GROUP BY EXTRACT(MONTH FROM `article`.`pub_date`), EXTRACT(YEAR FROM `article`.`pub_date`) ORDER BY `year` ASC, `month` ASC