在时间窗口之外立即获取行的有效方法
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 窗口之外的鼠标事件