Django - 跨查询集的DatetimeField的时间聚合

时间:2017-09-12 12:52:06

标签: python django django-1.11 aggregates django-mysql

(使用django 1.11.2,python 2.7.10,mysql 5.7.18)

如果我们想象一个简单的模型:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='Message'></div>

运行类似于以下内容的最优雅(最快)的方法是什么:

class Event(models.Model):
    happened_datetime = DateTimeField()
    value = IntegerField()

但是,这将能够为查询集的所有成员提取平均 时间 。类似的东西:

res = Event.objects.all().aggregate(
    Avg('happened_datetime')
)

是否可以直接在db上执行此操作?,即不为每个查询集成员运行长循环客户端?

编辑:

使用原始SQL可能有一个解决方案:

res = Event.objects.all().aggregate(
    AvgTimeOfDay('happened_datetime')
)

性能方面,在笔记本电脑上运行时间为0.015秒,约为23k行,未经优化等。假设可以产生准确/正确的结果,并且由于时间只是次要因素,可能我正在使用它?

2 个答案:

答案 0 :(得分:1)

在模型中添加另一个整数字段,该字段仅包含从happened_datetime中提取的一天中的小时。

创建/更新模型实例时,只要设置/更新happened_datetime,就需要相应地更新此新字段。您可以通过阅读datetime.datetime.hour来提取一天中的小时数。或者使用strftime根据自己的喜好创建一个值。

聚合应该按照你自己的建议进行。

编辑:

Django的ORM以Extract()为功能。适用于您的用例的文档示例:

>>> # How many experiments completed in the same year in which they started?
>>> Event.objects.aggregate(
...    happenend_datetime__hour=Extract('happenend_datetime', 'hour'))

(未经测试!) https://docs.djangoproject.com/en/1.11/ref/models/database-functions/#extract

答案 1 :(得分:0)

所以经过一番搜索并尝试......下面似乎有效。欢迎任何关于如何改进(或暗示为何完全错误)的评论! : - )

res = Event.objects.raw('''
SELECT id, sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from happened_datetime)))) AS average_time_of_day
FROM event_event
WHERE happened_datetime BETWEEN %s AND %s;''', [start_datetime, end_datetime])

print res[0].__dict__
# {'average_time_of_day': datetime.time(18, 48, 10, 247700), '_state': <django.db.models.base.ModelState object at 0x0445B370>, 'id': 9397L}

现在返回的ID是WHERE子句的日期时间范围内的最后一个对象的ID。我相信Django只是插入,因为“InvalidQuery:Raw查询必须包含主键”。

SQL系列函数调用的快速解释:

  1. 从所有日期时间字段中提取HH:MM:SS
  2. 通过time_to_sec将时间值转换为秒。
  3. 平均所有秒值
  4. 将平均秒值转换回时间格式(HH:MM:SS)
  5. 不知道为什么Django坚持要求返回微秒,但这并不是真正相关的。 (可能是实例化时间对象的本地ms?)

    性能说明:这似乎非常快,但我又没有测试过那一点。任何见解都将受到赞赏:)