在 Postgresql 中从模糊时间戳计算匹配数据点

sts*_*tsc 7 postgresql

我有一个包含多个不同类型的时间序列的表。来自不同系列的内聚点的时间戳不完全匹配(即差异可能长达一个小时)。

架构

下面是带有两个示例系列的架构:

CREATE TABLE series (id integer, series_type integer, charttime timestamp,
                     value integer, PRIMARY KEY (id));
INSERT INTO series VALUES (1, 1, '2018-03-01 12:10:00', 40),
    (2, 1, '2018-03-01 13:25:00', 30), (3, 1, '2018-03-01 14:10:00', 50);
INSERT INTO series VALUES (4, 2, '2018-03-01 11:20:00', 2), (5, 2, '2018-03-01 12:15:00', 6),
    (6, 2, '2018-03-01 13:00:00', 7), (7, 2, '2018-03-01 13:45:00', 1);

id |series_type |charttime           |value |
---|------------|--------------------|------|
1  |1           |2018-03-01 12:10:00 |40    |
2  |1           |2018-03-01 13:25:00 |30    |
3  |1           |2018-03-01 14:10:00 |50    |
4  |2           |2018-03-01 11:20:00 |2     |
5  |2           |2018-03-01 12:15:00 |6     |
7  |2           |2018-03-01 13:45:00 |1     |
6  |2           |2018-03-01 13:00:00 |7     |
Run Code Online (Sandbox Code Playgroud)

目标

目标是从另一个系列中选择一个系列以及最近的数据点。对于示例数据集,结果应该是:

charttime           |s1 |s2 |
--------------------|---|---|
2018-03-01 12:10:00 |40 |6  |
2018-03-01 13:25:00 |30 |1  |
2018-03-01 14:10:00 |50 |1  |
Run Code Online (Sandbox Code Playgroud)

第一种工作方法

我目前的方法是通过子查询从其他系列中选择最匹配的数据点:

SELECT l.charttime, l.value AS s1,
    ( SELECT r.value
      FROM series r
      WHERE ABS( EXTRACT( EPOCH FROM l.charttime - r.charttime ) / 3600 ) < 1
            AND r.series_type = 2
      ORDER BY ABS( EXTRACT( EPOCH FROM l.charttime - r.charttime )) ASC LIMIT 1 
    ) AS s2
FROM series l
WHERE l.series_type = 1
ORDER BY l.charttime ASC
Run Code Online (Sandbox Code Playgroud)

这似乎不是最好的方法,因为数据集非常庞大,因此执行许多子查询会减慢查询速度。

第二种方法

一个不同的想法是自连接表并过滤关闭数据时间戳:

SELECT l.charttime, l.value AS s1, r.charttime, r.value AS s2
FROM series l, series r
WHERE abs(EXTRACT(EPOCH FROM l.charttime - r.charttime) / 3600) < 1
      AND l.series_type = 1 AND r.series_type = 2

charttime           |s1 |charttime           |s2 |
--------------------|---|--------------------|---|
2018-03-01 12:10:00 |40 |2018-03-01 11:20:00 |2  |
2018-03-01 12:10:00 |40 |2018-03-01 12:15:00 |6  |
2018-03-01 12:10:00 |40 |2018-03-01 13:00:00 |7  |
2018-03-01 13:25:00 |30 |2018-03-01 13:45:00 |1  |
2018-03-01 13:25:00 |30 |2018-03-01 13:00:00 |7  |
2018-03-01 14:10:00 |50 |2018-03-01 13:45:00 |1  |
Run Code Online (Sandbox Code Playgroud)

那么问题是重复的数据点。第一列中的分组不起作用,因为s2无法选择最佳匹配。

有没有更好的方法?

Mic*_*een 4

在第二种方法中,通过自连接,您可以使用删除重复项row_number()

按 l.charttime 分区,按时间差排序并过滤 row_number = 1。

然而,我认为性能会很糟糕。由于笛卡尔连接,这将是一个 O(size(series 1) x size(series 2)) 操作。

在函数内部同时使用 l.charttime 和 r.charttime 也可能会造成麻烦。尝试重构(以伪代码)

    r.charttime < l.charttime + 3600
and r.charttime > l.charttime - 3600
Run Code Online (Sandbox Code Playgroud)

..并查看查询计划的外观。我猜想图表时间上有一个索引。没有一个方法就不会很快。事实上,两个部分索引,一个在系列 1 上,一个在系列 2 上可能会更好。