0

我正在尝试执行高级分析查询来驱动 Web 应用程序。我将 Hapi、Objection、Knex 和 Postgres 与 TimescaleDB 一起使用。对于典型的关系查询,这一切都运行良好。但是,我无法弄清楚如何执行这个涉及加入从 Postgres 生成的匿名表的聚合查询generate_series。我不得不求助于编写原始 SQL,而不是 Objection/Knex 查询生成器。我正在使用一些 Postgres 的内置函数,以及time_bucket来自 Timescale 的函数。time_bucket本质上是根据参数中指定的间隔创建数据的汇总。查看此链接以获取有关我正在尝试执行Gap Filling的更多信息。

这是查询,它使用异议模型上的原始方法工作。我相信像这样进行字符串插值会导致潜在的 SQL 注入。但是,我希望将其转换为 Objection/Knex 使用的查询构建器方法,因此它更多的是 JavaScript,而不是 SQL,这将解决 SQL 注入问题。

let errorHistorgram = await Errors
    .raw(`SELECT period AS daily, coalesce(count,0) AS count
    FROM generate_series(date '${startTS}', date '${today}', interval '1d') AS period
      LEFT JOIN (
        SELECT time_bucket('1d',timestamp)::date AS date, count(timestamp)
        FROM my_error_table
        WHERE severity = 'HIGH'
          AND timestamp >= '${startTS}' AND timestamp < '${today}'
          AND device_id = ${deviceId}
        GROUP BY date
      ) t ON t.date = period;`)
      .debug();

我已经用 Objection/Knex 做了几次尝试。这是我制作此查询的最成功尝试。但是,我认为 where 子句不在正确的位置。

let errorHistorgram = await Errors
    .query()
    .select(raw(`time_bucket('1 day', timestamp) AS daily, count(timestamp)`))
    .where('device_id', deviceId)
    .andWhere('timestamp', '>', startTS)
    .andWhere('severity', 'HIGH')
    .leftJoin(`generate_series(date ${startTS}, date ${today}, interval 1d) AS series`, 'series.date', 'my_error_table.timestamp')
    .debug();

使用.debug(),我可以看到下面发布的查询输出。

select time_bucket('1 day', timestamp) AS daily, count(timestamp)
from my_error_table
left join "generate_series(date 2018-11-08T15:35:33"."050Z, date 2018-11-15T15:35:33"."133Z, interval 1d)" as "series"
  on "series"."date" = my_error_table."timestamp"
where "device_id" = ? and "timestamp" > ? and "severity" = ?'

感谢您提供任何帮助,因为我没有使用 Objection 来执行此操作,并且找不到任何文档。

2018 年 11 月 15 日更新

我得到它来执行带有异议的查询。但是,我得到一个空数组作为结果。与我在上面制作的原始 SQL 查询不同(它确实给了我预期的结果),我只得到一个空数组作为查询生成器的输出。关于我做错了什么的任何想法。我试图将连接翻转为正确的连接,但没有运气。

 let errorHistorgram = await Errors
  .query()
  .select(raw(`time_bucket('1 day', timestamp) AS daily, count(timestamp)`))
  .where('device_id', deviceId)
  .andWhere('timestamp', '>', startTS)
  .andWhere('severity', 'HIGH')
  .groupBy('timestamp')
  .rightJoin(raw(`generate_series(date '${startTS}', date '${today}', interval '1d') AS series`), 'series.date', 'my_error_table.timestamp')
  .debug();

附件是 Debug 的 SQL 输出。

select time_bucket('1 day', timestamp) AS daily, count(timestamp) 
from my_errors_table
right join generate_series(date '2018-11-08', date '2018-11-15', interval '1d') AS series
on series = my_errors_table.timestamp
where device_id = ? and timestamp > ? and severity = ? 
group by timestamp
4

1 回答 1

0

Timescale 发布了一项名为Time Bucket Gapfill的新功能。它使这变得更加容易,因为您不再需要使用generate_seriesto gap 填充进行左连接。

我已经包含了一个示例,说明如何使用名为Errors. 函数的输入time_bucket_gapfill是存储桶大小、时间戳列名称、startTS 和 endTS。桶大小变量应该是一个带(不是单引号)的字符串,""对应于桶大小(例如:"10 seconds"、、、、"30 minutes")。and应该是 ISO 日期字符串。第二个 select 语句要求如果生成了一个桶,而桶中没有数据,它将输出 0。分桶需要根据您在语句中提供的聚合 SQL 函数正确汇总数据。"1 hour""1 day"startTSstopTSCOALESCEgroup byselect

import { raw } from 'objection';

const errors = await Errors
  .query()
  .select(
    raw("time_bucket_gapfill(?, timestamp, ?, ?) AS bucket", [bucketWidth, startTS, endTS]),
    raw('COALESCE(count(timestamp), 0) AS count'),
  ).where('device_id', deviceId)
  .andWhere('timestamp', '>=', startTS)
  .andWhere('timestamp', '<=', endTS)
  .groupBy('bucket')
  .orderBy('bucket');
于 2019-06-20T14:24:51.223 回答