Django视图中查询近7天每日预订量的数据库操作最佳实践咨询
Django View层数据库查询优化解答
原写法问题分析
第一种7次filter的写法
- 结论:完全不合理,且确实会触发7次独立的数据库查询
- 额外问题:你用
len()计算查询集长度的写法也有性能问题:len()会把符合条件的所有行全部加载到Django服务内存中再统计数量,远不如直接调用QuerySet.count()(直接在数据库层执行SELECT COUNT(*),不需要传输全量数据)效率高。
第二种全量遍历的写法
- 结论:比第一种写法性能更差,绝对不推荐
- 问题:该写法会把所有
is_reserved=True的预订数据全部加载到内存中遍历,如果你的业务预订量达到千级/万级以上,会直接占用大量内存,甚至导致服务OOM,完全违背了数据库本身擅长聚合运算的设计逻辑。
最佳实践方案
Django ORM原生支持分组聚合能力,你要的过去7天每日有效预订量,只需要1次数据库查询即可完成,性能远高于上述两种写法:
第一步:导入依赖
from django.db.models import Count from datetime import timedelta # 如果你的存储字段是DateTime类型而非Date类型,还需要导入: # from django.db.models.functions import TruncDate
第二步:执行分组聚合查询
# 先计算过去7天的起始日期 start_date = TODAY_DATE - timedelta(days=6) # 一次查询完成按日期分组统计 daily_reserve_counts = Reservations.objects.filter( is_reserved=True, date__range=(start_date, TODAY_DATE) ).values('date').annotate( daily_count = Count('id') # 按日期分组统计数量 ).order_by('date')
返回结果格式示例:
<QuerySet [{'date': datetime.date(2024,5,1), 'daily_count': 12}, {'date': datetime.date(2024,5,2), 'daily_count': 7}, ...]>
可选:补全无预订的日期
如果某一天没有有效预订,聚合结果默认不会返回该日期的记录,如果你需要保证返回7天的完整数据,可以自行构造默认值字典:
# 初始化7天的默认统计值为0 result = {TODAY_DATE - timedelta(days=i):0 for i in range(7)} # 把查询结果填充到默认字典中 for item in daily_reserve_counts: result[item['date']] = item['daily_count']
额外优化建议
- 可以给
date和is_reserved字段加联合索引,进一步加快查询速度 - 如果你的日期字段是DateTime类型,分组前需要先截断为日期:
daily_reserve_counts = Reservations.objects.filter( is_reserved=True, create_time__range=(start_date, TODAY_DATE) ).annotate( date = TruncDate('create_time') ).values('date').annotate( daily_count = Count('id') ).order_by('date')
内容的提问来源于stack exchange,提问作者Shawn
相关产品推荐
相关产品推荐

