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

时间:2018-09-04 01:23:24

标签: python django django-views django-queryset

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

也就是说,基本上将它们结合起来,所以每个对象都有每个日期范围的总和。

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()来实现自己的目标,但是我发现还有一种更Python化的方式来实现它。

2 个答案:

答案 0 :(得分:1)

据我所知,您似乎有两个几乎完全相同的查询,只是它们之间的区别是date__range。为什么不尝试使用Q-objects

在日期范围内使用SQL OR

您可以尝试以下操作:

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'))

旁注

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

答案 1 :(得分:1)

如果您想要多个行,则可以union two querysets像这样:

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查询就给您两行!


这是怎么回事?

最重要的是,在评估查询集之前(例如,在list()-ed,len()-ed,and so on上迭代),django不会执行任何SQL。因此,我们只是在构建SQL,而不是执行看起来像4个查询的操作!

大多数数据库(我认为甚至是sqlite)都有一些查询优化。即它将看到在两个查询中重复的事情,并且首先执行(以某种方式显示代码 在执行)。因此,我们应该(最接近)以最有效的方式进行此操作。


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