Django如何通过单次QuerySet查询获取不同concept_payment对应Temporal数据,减少查询次数
可行的优化方案
你完全可以通过单次查询拿到所有需要的数据,以下是两种常用实现方式:
方案1:单次查询后Python层分组(最简单通用)
公共过滤条件完全相同,你可以先一次性拉取所有符合公共条件的记录,再在内存中按concept_payment分组取首条即可:
from collections import defaultdict # 仅执行1次数据库查询,拉取所有符合公共条件的记录 temporal_qs = Temporal.objects.filter( rate_name=rate_name, date_start__month__lte=month_apply, date_end__month__gte=month_apply ).order_by('concept_payment', 'order') # 排序规则和你原有first()逻辑保持一致即可 # 按concept_payment分组,取每个分组的第一条记录 concept_map = defaultdict(lambda: None) for item in temporal_qs: if concept_map[item.concept_payment] is None: concept_map[item.concept_payment] = item
使用时直接从字典取值即可,和原有逻辑完全兼容:
energy_concepts = concept_map.get('Energy') demand_concepts = concept_map.get('Demand') other_concepts = concept_map.get('Others') taxes_concepts = concept_map.get('Taxes')
方案2:数据库层面直接取各分组首条(数据量大时更推荐)
如果符合条件的记录数量很大,不想把全量数据拉到内存,可以用Django的窗口函数直接在数据库层完成分组取首条的操作,仅返回每个concept_payment对应的第一条记录:
from django.db.models import F, Window from django.db.models.functions import RowNumber # 仅执行1次数据库查询,直接返回每个concept_payment的第一条记录 temporal_qs = Temporal.objects.filter( rate_name=rate_name, date_start__month__lte=month_apply, date_end__month__gte=month_apply ).annotate( row_num=Window( expression=RowNumber(), partition_by=[F('concept_payment')], order_by=F('order').asc(nulls_last=True) # 排序规则和原有first()逻辑保持一致即可 ) ).filter(row_num=1) # 转成字典方便后续取值 concept_map = {item.concept_payment: item for item in temporal_qs}
注意事项
- 两种方案均仅执行1次数据库查询,远优于原有N次查询的实现
- 代码中的
order_by规则请和你原有first()逻辑的排序规则保持一致,若原来默认按主键排序,替换为id即可,避免返回结果和原有逻辑不一致 - 如果需要获取所有
concept_payment的可选值,直接取concept_map.keys()就能拿到当前符合条件的所有concept_payment取值,无需额外查询
内容的提问来源于stack exchange,提问作者Lmex89
相关产品推荐
相关产品推荐

