如何聚合查询集中多个日期范围内的总和?



是否可以在一个QuerySet中注释多个日期范围内的.sum()

即,基本上将这些组合在一起,因此每个对象都有每个日期范围的总和。

query_set_week = DailyReports.objects.filter(
date__range=('2018-08-27', '2018-08-31')) 
.select_related('profile') 
.values('profile__user_id') 
.annotate(premium=Sum('total_field'),
first_name=F('profile__user__first_name'),
last_name=F('profile__user__last_name') 
.order_by('profile__agent_code')

query_set_year = DailyReports.objects.filter(
date__range=('2018-01-01', '2018-08-31')) 
.select_related('profile') 
.values('profile__user_id') 
.annotate(premium=Sum('total_field'),
first_name=F('profile__user__first_name'),
last_name=F('profile__user__last_name') 
.order_by('profile__agent_code')

两者都单独工作,但很难循环遍历和显示数据(例如,用户 - 周总计 - 年总计(,因为有人可能在年份筛选器中具有结果,但在周筛选器中没有结果。

编辑:我目前能够使用带有大量SQL语句的.raw()来实现我的目标,但我认为有一种更Pythonic的方法可以做到这一点。

如果需要多行,可以像这样合并两个查询集:

base_qs = DailyReports.objects 
.select_related('profile') 
.values('profile__user_id') 
.annotate(premium=Sum('total_field'),
first_name=F('profile__user__first_name'),
last_name=F('profile__user__last_name') 
.order_by('profile__agent_code')
query_set_week = base_qs.filter(date__range=('2018-08-27', '2018-08-31'))
query_set_year = base_qs.filter(date__range=('2018-01-01', '2018-08-31'))
query_set_week_and_year = query_set_week.union(query_set_year)

query_set_week_and_year应该给你两行,只执行一个SQL查询!


这是怎么回事?

最重要的是,django 在评估查询集之前不会执行任何 SQL(例如迭代、list()-ed、len()-ed 等(。所以我们只是在构造 SQL,而不是执行看起来像 4 个查询的东西!

大多数数据库(我认为甚至是sqlite(都有一些查询优化。 即,它将看到在两个查询中重复的事情并首先执行该位(以代码似乎正在执行的方式(。因此,我们应该以(足够近(最有效的方式做到这一点。


所以它不应该太耗时,我认为提供了一些可读的代码(?(!

据我所知,您似乎有两个几乎相同的查询,仅在date__range上有所不同。为什么不尝试使用Q对象在日期范围内使用 SQLOR

您可以尝试以下操作:

query_set_week_and_year = DailyReports.objects 
.filter(
Q(date__range=('2018-08-27', '2018-08-31')) | Q(date__range=('2018-01-01', '2018-08-31')) 
).select_related('profile') 
.values('profile__user_id') 
.annotate(
premium=Sum('total_field'),
first_name=F('profile__user__first_name'),
last_name=F('profile__user__last_name'
).order_by('profile__agent_code')

Q对象简述

Q 对象允许您通过二进制&|运算符应用ANDOR筛选条件。(但您只需要它们进行OR操作,因为AND由过滤器函数中的逗号处理(

例如,查找名字或姓氏以"A"开头的所有用户:

User.objects.filter(Q(firstname__startswith='A') | Q(lastname__startswith='A'))

旁注

似乎第一行date__range包含在另一行中:2018-08-27-->2018-08-312018-01-01-->2018-08-31范围内。如果这是您关心的两个日期范围,那么您只需要第二个查询..?

最新更新