在时间窗口之外立即获取行的有效方法

Posted

技术标签:

【中文标题】在时间窗口之外立即获取行的有效方法【英文标题】:Efficient way to get rows immediately outside of a time window 【发布时间】:2020-03-05 14:59:50 【问题描述】:

我们将时间序列数据存储在 clickhouse 表中,类似于:

timestamp             value
2020-03-05 11:03:00    2
2020-03-05 11:12:00    3
2020-03-05 11:13:00    4
2020-03-05 11:27:00    5
2020-03-05 11:31:00    6
2020-03-05 11:39:00    7

在可视化这些数据时,我们会请求一个时间范围,例如 2020-03-05 11:15:00 - 2020-03-05 11:30:00

在这个范围内选择数据很容易,但是对于可视化更有用的是同时获得两边的点,即:

2020-03-05 11:12:00    3
2020-03-05 11:13:00    4
2020-03-05 11:27:00    5
2020-03-05 11:31:00    6

在 clickhouse 中有没有一种有效的方法来做到这一点?目前我正在(可能)进行 3 个单独的查询:

    选择范围内的数据:
select * from data where timestamp >= "from" and timestamp <= "to" order by timestamp
    如果第一个点的时间戳 != "from" 时间戳:
select * from data where timestamp < "from" order by timestamp desc limit 1
    如果最后一点的时间戳 != "to" 时间戳:
select * from data where timestamp > "to" order by timestamp limit 1

如果可以在一个查询中得到它,那就太好了。

【问题讨论】:

【参考方案1】:

看起来只需将所有三个查询合并为一个,并稍微更改比较运算符:

SELECT * 
FROM (
  SELECT *
  FROM
  (
      /* test data */
      SELECT data.1 AS timestamp, data.2 AS value
      FROM (SELECT arrayJoin([(toDateTime('2020-03-05 11:03:00'), 2), (toDateTime('2020-03-05 11:12:00'), 3), (toDateTime('2020-03-05 11:13:00'), 4), (toDateTime('2020-03-05 11:27:00'), 5), (toDateTime('2020-03-05 11:31:00'), 6), (toDateTime('2020-03-05 11:39:00'), 7)]) AS data)
  )
  WHERE timestamp > '2020-03-05 11:15:00' AND timestamp < '2020-03-05 11:30:00'
  UNION ALL
  SELECT DISTINCT *
  FROM (
    SELECT *
    FROM
    (
        /* test data */
        SELECT data.1 AS timestamp, data.2 AS value
        FROM (SELECT arrayJoin([(toDateTime('2020-03-05 11:03:00'), 2), (toDateTime('2020-03-05 11:12:00'), 3), (toDateTime('2020-03-05 11:13:00'), 4), (toDateTime('2020-03-05 11:27:00'), 5), (toDateTime('2020-03-05 11:31:00'), 6), (toDateTime('2020-03-05 11:39:00'), 7)]) AS data)
    )
    WHERE timestamp <= '2020-03-05 11:15:00'
    ORDER BY timestamp DESC
    LIMIT 1
    UNION ALL
    SELECT *
    FROM
    (
        /* test data */
        SELECT data.1 AS timestamp, data.2 AS value
        FROM (SELECT arrayJoin([(toDateTime('2020-03-05 11:03:00'), 2), (toDateTime('2020-03-05 11:12:00'), 3), (toDateTime('2020-03-05 11:13:00'), 4), (toDateTime('2020-03-05 11:27:00'), 5), (toDateTime('2020-03-05 11:31:00'), 6), (toDateTime('2020-03-05 11:39:00'), 7)]) AS data)
    )
    WHERE timestamp >= '2020-03-05 11:30:00'
    ORDER BY timestamp ASC
    LIMIT 1))
ORDER BY timestamp;
/* result
┌───────────timestamp─┬─value─┐
│ 2020-03-05 11:13:00 │     4 │
│ 2020-03-05 11:27:00 │     5 │
│ 2020-03-05 11:31:00 │     6 │
└─────────────────────┴───────┘
*/
..
WHERE timestamp > '2020-03-05 11:13:00' AND timestamp < '2020-03-05 11:30:00'
..
/* result
┌───────────timestamp─┬─value─┐
│ 2020-03-05 11:13:00 │     4 │
│ 2020-03-05 11:27:00 │     5 │
│ 2020-03-05 11:31:00 │     6 │
└─────────────────────┴───────┘
*/ 
..
WHERE timestamp > '2020-03-05 11:15:00' AND timestamp < '2020-03-05 11:31:00'
..
/* result
┌───────────timestamp─┬─value─┐
│ 2020-03-05 11:13:00 │     4 │
│ 2020-03-05 11:27:00 │     5 │
│ 2020-03-05 11:31:00 │     6 │
└─────────────────────┴───────┘
*/
..
WHERE timestamp > '2020-03-05 11:27:00' AND timestamp < '2020-03-05 11:31:00'
..
/* result
┌───────────timestamp─┬─value─┐
│ 2020-03-05 11:27:00 │     5 │
│ 2020-03-05 11:31:00 │     6 │
└─────────────────────┴───────┘
*/
..
WHERE timestamp > '2020-03-05 11:28:00' AND timestamp < '2020-03-05 11:28:00'
..
/* result
┌───────────timestamp─┬─value─┐
│ 2020-03-05 11:27:00 │     5 │
│ 2020-03-05 11:31:00 │     6 │
└─────────────────────┴───────┘
*/
..
WHERE timestamp > '2020-03-05 11:31:00' AND timestamp < '2020-03-05 11:31:00'
..
/*
result
┌───────────timestamp─┬─value─┐
│ 2020-03-05 11:31:00 │     6 │
└─────────────────────┴───────┘
*/

【讨论】:

谢谢,这看起来很合理。我会用一些真实的数据来测试它。但是为什么SELECT DISTINCT 在两个外围点上? SELECT DISTINCT 需要在 "from" == "to" 的情况下(例如,WHERE timestamp > '2020- 03-05 11:31:00' AND 时间戳 )。 这很好用。当然不比单个查询快,但节省了网络旅行。【参考方案2】:

我会通过一些函数来扩展范围 toStartOfTenMinutes('2020-03-05 11:15:00) toStartOfTenMinutes('2020-03-05 11:30:00')+600 或 -600 +600

并在客户端过滤过多的行。 因为 3 个查询比 1 个慢。

【讨论】:

我已经尝试过这种方法,但问题是我们只在值发生变化时才获取行。这意味着某些数据在点之间可能有数小时甚至数天,而有些数据每秒都在变化。设置扩展范围会在某些系列中失分,而在其他系列中获得太多额外积分。 @JeffWilliams 您可以使用 toStartOfInterval() 并指定自定义间隔(天、小时、秒等)

以上是关于在时间窗口之外立即获取行的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

在内置“滚动”之外对 Pandas Dataframe 进行滚动窗口操作的最佳方法?

在 Python 中获取 Tkinter 窗口之外的鼠标事件

Spark DataSet 有效地获取整行的长度大小

如何更新主表当前行的输入类型文本中的值从模态窗口表中获取值

使用 TimescaleDB 获取每个组/聚合函数的最新/最新版本的版本化时间序列的有效方法

Redshift:获取行的排名,按条件过滤