【发布时间】:2016-02-16 15:28:09
【问题描述】:
我在最后一天试图从我的数据库中获取时间序列的聚合。我尝试使用 Django ORM,但很快放弃并返回 SQL。我认为没有办法将 PSQL generate_series 与它一起使用,我认为他们更喜欢您使用 itertools 或 python 中的其他方法。
我有一个很像这样的模型:
class Vote(models.Model):
value = models.IntegerField(default=0)
timestamp = models.DateTimeField('date voted', auto_now_add=True)
location = models.ForeignKey('location', on_delete=models.CASCADE)
我想要做的是随着时间的推移显示一系列指标 - 目前,当前用户在当天每小时的聚合。用户设置了时区(默认为“美国/芝加哥”)。我一直在使用 postgres 查询,插入大量的 AS TIME ZONE 强制转换,以解决查询的边界和返回值。我昨晚深夜让它返回了正确的结果,但今天早上,它又关闭了。我知道我正在做一些非常愚蠢的事情。由于 Postgres 处理 AT TIME ZONE 的奇怪方式(更正为 UTC 而不是 FROM),我什至使用了双重转换时间戳
再次,我想显示用户当天每个小时的聚合桶,直到/包括“现在”。
这是我当前的查询:
WITH hour_intervals AS (
SELECT * FROM generate_series(date_trunc('day',(SELECT TIMESTAMP 'today' AT TIME ZONE 'UTC' AT TIME ZONE %s)), (LOCALTIMESTAMP AT TIME ZONE 'UTC' AT TIME ZONE %s), '1 hour') start_time
)
SELECT f.start_time,
COUNT(id) total,
COUNT(CASE WHEN value > 0 THEN 1 END) AS positive_votes,
COUNT(CASE WHEN value = 0 THEN 1 END) AS indifferent_votes,
COUNT(CASE WHEN value < 0 THEN 1 END) AS negative_votes,
SUM(CASE WHEN value > 0 THEN 2 WHEN value = 0 THEN 1 WHEN value < 0 THEN -4 END) AS score
FROM votes_vote m
RIGHT JOIN hour_intervals f
ON m.timestamp AT TIME ZONE %s >= f.start_time AND m.timestamp AT TIME ZONE %s < f.start_time + '1 hour'::interval
AND m.location_id = %s
GROUP BY f.start_time
ORDER BY f.start_time
调试信息Django 1.9.2,我的 settings.py 有 USE_TZ=TruePostgres 9.5.2,我的 django 登录角色有
ALTER ROLE yesno_django
SET client_encoding = 'utf8';
ALTER ROLE yesno_django
SET default_transaction_isolation = 'read committed';
ALTER ROLE yesno_django
SET TimeZone = 'UTC';
更新 再摆弄一下查询,现在这是今天投票的有效查询...
WITH hour_intervals AS (
SELECT * FROM generate_series((SELECT TIMESTAMP 'today' AT TIME ZONE 'UTC'), (LOCALTIMESTAMP AT TIME ZONE 'UTC' AT TIME ZONE %s), '1 hour') start_time
)
SELECT f.start_time,
COUNT(id) total,
COUNT(CASE WHEN value > 0 THEN 1 END) AS positive_votes,
COUNT(CASE WHEN value = 0 THEN 1 END) AS indifferent_votes,
COUNT(CASE WHEN value < 0 THEN 1 END) AS negative_votes,
SUM(CASE WHEN value > 0 THEN 2 WHEN value = 0 THEN 1 WHEN value < 0 THEN -4 END) AS score
FROM votes_vote m
RIGHT JOIN hour_intervals f
ON m.timestamp AT TIME ZONE %s >= f.start_time AND m.timestamp AT TIME ZONE %s < f.start_time + '1 hour'::interval
AND m.location_id = %s
GROUP BY f.start_time
ORDER BY f.start_time
为什么我之前在昨晚 7 点到 10 点之间完美运行的查询,但今天却失败了?我是否应该期望这个新查询也会失败?
有人可以解释我第一次(或每次)出错的地方吗?
【问题讨论】:
-
为什么不能使用
DATE_TRUNC? Django 有使用它的内置选项。 -
@GwynBleidD 喜欢这样吗?
votes = Vote.objects.filter(location=l).filter(timestamp__date=timezone.now().date()).extra({"hour":"date_trunc('hour',timestamp)"}).values("hour").order_by().annotate(score=score_annotation, count=Count('id'))我认为它已经接近了——我将更多地使用这种方法。谢谢! -
我从 SQL 中获取 date_trunc,但如果您不必严格使用您的方法来生成该查询,我可以发布完整的答案,创建几乎相同的结果。
-
@GwynBleidD 我很想看看——实际上,我在上面发布的 QuerySet 不起作用。
标签: python django postgresql timezone