Django - 跨查询集的 DatetimeField 时间聚合
Django - Time aggregates of DatetimeField across queryset
(使用 django 1.11.2,python 2.7.10,mysql 5.7.18)
如果我们想象一个简单的模型:
class Event(models.Model):
happened_datetime = DateTimeField()
value = IntegerField()
运行 类似于以下内容的最优雅(也是最快)的方法是什么:
res = Event.objects.all().aggregate(
Avg('happened_datetime')
)
但这将能够提取查询集所有成员的平均时间。类似于:
res = Event.objects.all().aggregate(
AvgTimeOfDay('happened_datetime')
)
是否可以直接在数据库上执行此操作?即,无需 运行为每个查询集成员设置长循环客户端?
编辑:
按照这些思路,使用原始 SQL:
可能有一个解决方案
select sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from happened_datetime)))) from event_event;
性能方面,这 运行s 在 0.015 秒内用于笔记本电脑上的 ~23k 行,未优化等。假设 可以 产生 accurate/correct 结果,因为时间只是次要因素,我可以使用它吗?
向您的模型添加另一个整数字段,该字段仅包含从 happened_datetime
.
中提取的一天中的小时
当 creating/updating 模型实例时,只要 happened_datetime
为 set/updated,您就需要相应地更新此新字段。例如,您可以通过阅读 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
所以经过一些搜索和尝试.. 下面似乎有效。欢迎任何关于如何改进的评论(或暗示为什么它是完全错误的)! :-)
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 query must include the primary key".
SQL 系列函数调用的快速解释:
- 从所有日期时间字段中提取 HH:MM:SS
- 通过 time_to_sec.
将 time 值转换为秒
- 平均所有秒值
- 将平均秒值转换回时间格式 (HH:MM:SS)
不知道为什么 Django 坚持返回微秒,但这并不真正相关。 (也许是实例化时间对象的本地毫秒?)
性能说明:这似乎非常快,但我又没有测试过那一点。任何见解将不胜感激:)
(使用 django 1.11.2,python 2.7.10,mysql 5.7.18)
如果我们想象一个简单的模型:
class Event(models.Model):
happened_datetime = DateTimeField()
value = IntegerField()
运行 类似于以下内容的最优雅(也是最快)的方法是什么:
res = Event.objects.all().aggregate(
Avg('happened_datetime')
)
但这将能够提取查询集所有成员的平均时间。类似于:
res = Event.objects.all().aggregate(
AvgTimeOfDay('happened_datetime')
)
是否可以直接在数据库上执行此操作?即,无需 运行为每个查询集成员设置长循环客户端?
编辑:
按照这些思路,使用原始 SQL:
可能有一个解决方案select sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from happened_datetime)))) from event_event;
性能方面,这 运行s 在 0.015 秒内用于笔记本电脑上的 ~23k 行,未优化等。假设 可以 产生 accurate/correct 结果,因为时间只是次要因素,我可以使用它吗?
向您的模型添加另一个整数字段,该字段仅包含从 happened_datetime
.
当 creating/updating 模型实例时,只要 happened_datetime
为 set/updated,您就需要相应地更新此新字段。例如,您可以通过阅读 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
所以经过一些搜索和尝试.. 下面似乎有效。欢迎任何关于如何改进的评论(或暗示为什么它是完全错误的)! :-)
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 query must include the primary key".
SQL 系列函数调用的快速解释:
- 从所有日期时间字段中提取 HH:MM:SS
- 通过 time_to_sec. 将 time 值转换为秒
- 平均所有秒值
- 将平均秒值转换回时间格式 (HH:MM:SS)
不知道为什么 Django 坚持返回微秒,但这并不真正相关。 (也许是实例化时间对象的本地毫秒?)
性能说明:这似乎非常快,但我又没有测试过那一点。任何见解将不胜感激:)