获取每月最后一条记录的值(Django)

时间:2016-10-11 10:45:18

标签: mysql django django-queryset

我的模特:

class Transaction (models.Model):
    transaction_id = models.AutoField(primary_key=True)
    net_monthly_transaction = models.DecimalField(max_digits = 10, decimal_places = 2, default=0)
    # deposit or withdrawal (withdrawal with negative value)
    amount = models.DecimalField(max_digits = 10, decimal_places = 2)
    time_stamp = models.DateTimeField(default=datetime.now, blank=True)


    def __str__(self):              # __unicode__ on Python 2
        return str(self.time_stamp)  + str(self.amount) + str(self.net_monthly_transaction)

我的目标是从每个月的最后一个条目中获取net_monthly_transaction的值。

在S.O.的帮助下我设法做到这一点:

truncate_date = connection.ops.date_trunc_sql('month', 'time_stamp')
        lem = Transaction.objects.extra({'month':truncate_date}).values('month').annotate(last_record=Max('time_stamp')).values_list('net_monthly_transaction', flat=True)

以上查询假设从每个月的max time_stamp获取net_monthly_transaction的值。

但它没有。

如果我一个接一个地创建十月的三个条目:

  1. net_monthly_transaction = 3000
  2. net_monthly_transaction = 4000
  3. net_monthly_transaction = 5000
  4. 查询将返回所有3个值。

    另一方面:

    1. net_monthly_transaction = 3000
    2. net_monthly_transaction = 2000
    3. net_monthly_transaction = 1000
    4. 然后只返回值3000。

      因此根据net_monthly_transaction的大小设置某个条件。关于如何解决这个问题,我有点迷茫。

      有人可以提供一些指示。

      提前致谢。

2 个答案:

答案 0 :(得分:2)

我将通过使用两个查询集来解决这个问题(除非以下更简单的方法是一个选项)。只要您未明确评估last_entries,评估transactions时就会产生一次查询。

from django.db.models import Max
from django.db.models.functions import TruncMonth

# Selects last time_stamp for each month
last_entries = (Transaction.objects
    .annotate(tx_month=TruncMonth('time_stamp'))
    .values('tx_month')
    .annotate(last_entry=Max('time_stamp'))
    .values_list('last_entry', flat=True))

# Selects transactions with time_stamps matching last_entries
# ie. last transaction in each month
transactions = Transaction.objects.filter(
    time_stamp__in=last_entries
)

transactions是一个普通的查询集,包含每个月的最后一个Transaction实例。如果您想要一个简单的net_monthly_transaction值列表而没有其他信息,您可以添加:

net_values = transactions.values_list(
    'net_monthly_transaction', flat=True
)

要特别注意的一件事是,如果两个条目具有相同的时间戳,则它们都将出现在结果集中。

更简单的方法

如果net_monthly_transaction只是给定月份的所有amount的总和,那么您可以使用类似这样的内容而不是上述

from django.db.models import Sum
from django.db.models.functions import TruncMonth

transactions = (Transaction.objects
    .annotate(month=TruncMonth('time_stamp'))
    .values('month')
    .annotate(month_net=Sum('amount')))

现在transactions包含代表每个月最后一笔交易的词汇。每个字典都有一个包含月份的month键和一个包含月份净交易的month_net键。作为奖励,您不必担心具有相同时间戳的条目。

当然,如果net_monthly_transaction是更复杂计算的结果,那么这可能不是一个选项。

您的初步方法

您的初始查询无效,主要有两个原因。

  1. 这种查询通常依赖于将表连接到自身或WHERE子句中的子查询。我不知道使用Django的ORM使用单个查询集进行任何一种方法的好方法,除非您在extra()中使用填充原始SQL或沿着这些行填充原始SQL。但是,如果你按照我们上面的方式使用两个查询集,那么生成一个WHERE子句子查询是微不足道的。

  2. 以这种方式使用values_list()是没有意义的

    truncate_date = connection.ops.date_trunc_sql('month', 'time_stamp')
    lem = Transaction.objects.extra({'month':truncate_date}).values('month').annotate(last_record=Max('time_stamp'))
    

    到目前为止,它与上面使用的last_entries查询集类似。我们为这几个月中的每一个选择了唯一的月份值和最后一个time_stamp。

    当我们添加.values_list('net_monthly_transaction', flat=True)时,我们告诉查询构建器我们只关心net_monthly_transaction字段,因此它抛弃了其他所有内容并产生类似这样的内容

    SELECT "transaction"."net_monthly_transaction"
    FROM "transaction"
    GROUP BY "transaction"."net_monthly_transaction"
    

    GROUP BYextraannotate调用中剩下的唯一内容,即使已经更改,也不会执行我们想要的操作。< / p>

答案 1 :(得分:0)

如果要获取当月的最后一条记录,请首先过滤该月的所有记录,然后按升序排序,并选择应该是最新记录的最后一条记录。以下查询应该按此顺序工作。

Transaction.objects.filter(time_stamp__month=month_you_are_checking).order_by('time_stamp').last()

P.S。未经测试。