【问题标题】:Annotate Custom SQL Function (similar to date_trunc) to Django ORM Queryset将自定义 SQL 函数(类似于 date_trunc)注释到 Django ORM 查询集
【发布时间】:2018-03-23 03:00:14
【问题描述】:

我正在使用timescaledb,它基本上只是postgres 的扩展。它带有一个名为time_bucket 的SQL 函数。我想将此函数与 ORM 结合使用,生成如下查询:

SELECT
  time_bucket('1 minute', time) AS tb,
  AVG(s0) 
FROM measurements
WHERE
  time >= to_timestamp(1) AND
  time <= to_timestamp(2)
GROUP BY tb
ORDER BY tb ASC;

models.py:

class Measurement(models.Model):

    device_id = models.IntegerField(primary_key=True)
    time = models.DateTimeField()
    s0 = models.FloatField(blank=True, null=True)
    s1 = models.FloatField(blank=True, null=True)

到目前为止我的尝试:

class TimeBucket(Func):

    function = 'time_bucket'
    template = '%(function)s(\'{bucket_width}\', %(expressions)s)'.format(bucket_width='1 minute')


(Measurement.objects
    .values('time')
    .annotate(tb=TimeBucket('time'))
    .annotate(s_desc=Avg('s0'))
    .filter(
        time__gte=datetime.fromtimestamp(start),
        time__lte=datetime.fromtimestamp(end))
    .order_by('tb')
)

结果:

SELECT
  "measurements"."time",
  time_bucket('1 minute', "measurements"."time") AS "tb",
  (AVG("measurements"."s0")) AS "s_desc"
FROM "measurements"
WHERE (
  "measurements"."time" <= 2447-10-02 14:17:01+00:00 AND 
  "measurements"."time" >= 1970-01-01 00:00:01+00:00
)
GROUP BY "measurements"."time", time_bucket('1 minute', "measurements"."time")
ORDER BY "tb" ASC

如你所见,还剩下两个难看的点:

  • 如何在GROUP BY 中使用别名tb 而不是重复它?
  • 我只需要查询time_buckets0。如何在不中断查询的情况下摆脱time

【问题讨论】:

    标签: python django postgresql django-orm


    【解决方案1】:

    un-truncatedtime 只能在过滤器之前第一个annotate(...) 使用,不能再使用。 p>

    必须在.values()(例如.values('tb'))中在任何聚合函数之前使用截断时间注释。

    qs = (
        Measurement.objects
        .filter(...)
        .annotate(tb=TimeBucket('time'))
        .values('tb')
        .annotate(s_desc=Avg('s0'))
        .order_by('tb')
    )
    

    About alias in GROUP BY:别名在所有数据库中一般只能用在 ORDER BY 中,但只有特定数据库允许在 GROUP BY 或 WHERE 中使用(仅 MySQL 和 Postgresql)。这可能是因为 GROUP BY 和 WHERE 子句在 SELECT 子句之前进行评估。这可以被子查询绕过,但根本没有用。我确信每个现代数据库驱动程序的标准查询计划优化器都会重用来自 GROUP BY 的辅助表达式,并且它永远不会重复评估该函数。如果 SQL 是手动编写并由人类读取,则别名很有用,但在 Django ORM 编译器中通过别名仅对某些后端实现它没有用处。


    编辑:此解决方案适用于 Django >= 1.11,并且也在 Django 3.0 中进行了测试。

    (关于 Django 版本的信息不正确。我不记得 Django

    【讨论】:

    • 我知道这是旧的,但我发现这仍然包括 time 作为查询组的一部分。为什么会这样?
    • @Rhubarrbb 我修复并扩展了答案。最初的简单答案是正确的。关于 Django 版本的第一次旧编辑无效。
    猜你喜欢
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-22
    • 2020-04-07
    • 1970-01-01
    • 2018-06-17
    相关资源
    最近更新 更多