【问题标题】:Django aggregate count of records per dayDjango每天的总记录数
【发布时间】:2013-05-26 16:52:53
【问题描述】:

我有一个 django 应用程序正在做一些日志记录。我的模型如下所示:

class MessageLog(models.Model):
    logtime = models.DateTimeField(auto_now_add=True)
    user = models.CharField(max_length=50)
    message = models.CharField(max_length=512)

想要做的是获取一周中每天记录的平均消息数,以便我可以看到哪些日子最活跃。我设法编写了一个查询,该查询每天提取消息总数:

for i in range(1, 8):
    MessageLog.objects.filter(logtime__week_day=i).count()

但我无法计算查询中的平均值。我现在拥有的是:

for i in range(1, 8):
    MessageLog.objects.filter(logtime__week_day=i).annotate(num_msgs=Count('id')).aggregate(Avg('num_msgs'))

由于某种原因,这每天都会返回 1.0。我查看了它正在生成的 SQL,它是:

SELECT AVG(num_msgs) FROM (
SELECT 
`myapp_messagelog`.`id` AS `id`, `myapp_messagelog`.`logtime` AS `logtime`, 
`myapp_messagelog`.`user` AS `user`, `myapp_messagelog`.`message` AS `message`, 
COUNT(`myapp_messagelog`.`id`) AS `num_msgs` 
FROM `myapp_messagelog` 
WHERE DAYOFWEEK(`myapp_messagelog`.`logtime`) = 1 
GROUP BY `myapp_messagelog`.`id` ORDER BY NULL
) subquery

我认为问题可能来自 GROUP BY id,但我不太确定。有人有什么想法或建议吗?提前致谢!

【问题讨论】:

    标签: python django orm


    【解决方案1】:

    您列出的查询总是给出 1 的原因是因为您没有按日期分组。基本上,您已经要求数据库获取一周中给定日期的MessageLog 行。对于每个这样的行,计算它有多少个 id(总是 1)。然后取所有这些计数的平均值,当然也是 1。

    通常,您需要在annotateaggregate 部分之前使用values 子句对MessageLog 行进行分组。但是,由于您的 logtime 字段是日期时间而不仅仅是日期,我不确定您是否可以直接使用 Django 的 ORM 来表达。您绝对可以使用extra 子句来做到这一点,如here 所示。或者,如果您愿意,可以在 SQL 中声明一个视图,其中包含尽可能多的聚合和平均数学,并为它声明一个非托管模型,然后正常使用 ORM。

    因此,extra 字段用于获取实际每天的记录总数,但不处理聚合计算注释的平均值。我认为这可能已经从您必须使用原始 SQL 查询的模型中充分抽象出来,或者至少我找不到任何可以使其在一次调用中工作的东西。

    也就是说,您已经知道如何在问题中显示的简单查询中获取每个工作日的总记录数。

    这个查询会告诉你在给定的工作日有多少不同的日期记录:

    MessageLog.objects.filter(logtime__week_day=i).dates('logtime', day').count()
    

    因此,您可以改为在 Python 中进行平均数学运算,这可能比尝试正确使用 SQL 更简单。

    或者,此查询将在一个查询中为您获取所有工作日的原始消息数,而不是 for 循环:

    MessageLog.objects.extra({'weekday': "dayofweek(logtime)"}).values('weekday').annotate(Count('id'))
    

    但是我无法获得一个很好的查询来为您提供注释的每个工作日的不同日期的计数 - 日期查询集失去了处理注释调用的能力,并且注释 extra 值不会'似乎也没有用。

    考虑到 SQL 表达式并不难,这非常棘手。

    【讨论】:

    • 感谢您的解释!这很有意义。我曾尝试使用额外的子句,但是现在我遇到了 SQL 错误。我将查询修改为: MessageLog.objects.filter(logtime__week_day=i) .extra({'date_logged': "date(logtime)"}).values('date_logged') .annotate(num_msgs=Count('id' )) .aggregate(Avg('num_msgs')) 我得到的错误是“您的 SQL 语法有错误;请查看与您的 MySQL 服务器版本相对应的手册,以了解在 'FROM (SELECT ( date(logtime)) AS date_logged, COUNT(myapp_messagelog.`id' at line 1")
    • 是的,这不太行,是吗?聚合子句将其丢弃 - 它适用于注释。您可以通过几种不同的方式计算每个工作日的消息总数,但我找不到让 Django ORM 在一次调用中完成此操作的方法。我会将我想出的一些内容编辑到我的答案中。
    • 我实际上最终创建了一个视图(实际上 mysql 需要 2 个),然后按照您的建议创建了一个非托管模型,并且效果很好。我将添加另一篇文章,解释我稍后为那些可能感兴趣的人做了什么。再次感谢您的帮助!
    【解决方案2】:

    我对日期时间字段做了类似的事情,但是对额外的值进行注释确实对我有用。我有一个记录模型,其中包含一个日期时间字段“created_at”和一个“my_value”字段,我想获得平均值。

    from django.db.models import Avg
    
    qs = Record.objects.extra({'created_day':"date(created_at)"}).\
        values('created_day').\
        annotate(count=Avg('my_value'))
    

    以上将按“created_at”字段中日期时间值的日期分组。

    【讨论】:

      【解决方案3】:
      queryset.extra(select={'day': 'date(logtime)'}).values('day').order_by('-day').annotate(Count('id'))
      

      【讨论】:

      • 您能否解释一下答案将如何/为什么解决相关问题,这将为您的答案增加价值,而不是发布仅代码的答案。
      猜你喜欢
      • 1970-01-01
      • 2018-11-15
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 2011-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多