查询Postgres的时间序列

时间:2014-02-24 01:20:47

标签: sql postgresql time-series analytics

我正在使用Postgres来跟踪时间序列数据。

---------
|ts|value|
|--------|
|1 |5    |
|--------|
|2 |2    |
|--------|
|5 |10   |
----------

值字段的含义是“自上次时间戳以来发生的事件数”。例如,在3-5秒内,有10个事件,如ts 5的值所示。

我们的用户可能会问:“给我一个0-6秒的折线图,其数据点间隔为4秒”。因此,我们将显示两个数据点,分别为0和4秒,每个数据点总结直到下一个点的事件数。因此折线图将使用此数据:

---------
|ts|value|
|--------|
|0 |7    |
|--------|
|4 |10   |
----------

(因为5 + 2 = 7和10 = 10)

我们使用此查询来获取此数据。用户可以询问任意开始(时间),结束(时间)和间隔值。 ts也是整数类型(但可以调整为时间戳)。

SELECT start+round((ts-start)/interval)*interval as ts1, sum(value)
FROM events
WHERE ts >= start AND ts <= end
GROUP BY ts1
  1. 这是正确的查询,更重要的是正确的方法吗?
  2. 我假设ts上的索引会被忽略,我能做些什么呢?
  3. 无论如何都要在查询中添加线性插值逻辑,以便在3-5秒(在原始表中)中发生的10个事件不会完全到第二个4(结果集),因为其中第三个实际发生了在第二个3(原始数据)?
  4. 您可以使用此sqlfiddle

1 个答案:

答案 0 :(得分:2)

您目前正在这样做的一个问题是它不会生成 数据点在任何没有任何样本数据的invervals中。例如,如果 用户希望从0到10秒的图表以1为步长,然后您的图表将没有任何图表 5.可能在你的用例中无关紧要。

另一个问题,如你所说,能够使用某种方式会很好 线性插值,以便在分辨率的情况下归因于测量 请求的图表大于可用数据。

要解决其中的第一个问题,而不是纯粹从样本表中选择数据, 我们可以将数据与生成的与用户匹配的系列连接在一起 请求。后者可以使用以下方法生成:

SELECT int4range(rstart, rstart+1) AS srange 
FROM generate_series(0,10,1) AS seq(rstart)

上述查询将生成一系列范围,从步长到0到10 输出如下:

 srange
---------
 [0,1)
 [1,2)
 [2,3)
 [3,4)
 [4,5)
 [5,6)
 [6,7)
 [7,8)
 [8,9)
 [9,10)
 [10,11)
(11 rows)

我们可以使用&amp;&amp;和&amp;&amp;运算符(在重叠时过滤)。

第二点可以通过计算每个数据行的比例来解决 它落入每个样本窗口。

以下是完整查询:

SELECT lower(srange) AS t,
    sum (CASE 
        -- when data range is fully contained in sample range
        WHEN drange <@ srange THEN value
        -- when data range and sample range overlap, calculate the ratio of the intersection
        -- and use that to apportion the value
        ELSE CAST (value AS DOUBLE PRECISION) * (upper(drange*srange) - lower(drange*srange)) / (upper(drange)-lower(drange))
    END) AS value
FROM (
    -- Generate the range to be plotted (the sample ranges).
    -- To change the start / end of the range, change the 1st 2 arguments
    -- of the generate_series. To change the step size change BOTH the 3rd
    -- argument and the amount added to rstart (they must be equal).
    SELECT int4range(rstart, rstart+1) AS srange FROM generate_series(0,10,1) AS seq(rstart)
) AS s
LEFT JOIN (
    -- Note the use of the lag window function so that for each row, we get
    -- a range from the previous timestamp up to the current timestamp
    SELECT int4range(coalesce(lag(ts) OVER (order by ts), 0), ts) AS drange, value FROM data
) AS d ON srange && drange
GROUP BY lower(srange)
ORDER BY lower(srange)

结果:

 t  |      value
----+------------------
  0 |                5
  1 |                2
  2 | 3.33333333333333
  3 | 3.33333333333333
  4 | 3.33333333333333
  5 |
  6 |
  7 |
  8 |
  9 |
 10 |
(11 rows)

在此查询中,不太可能在ts上使用任何索引,并且 如果数据表很大,那么性能就会很糟糕。

有些事情你可以尝试帮助解决这个问题。一个建议可能是 重新设计数据表,使第一列包含时间范围 数据样本,而不仅仅是结束时间,然后你可以添加一个 范围指数。然后,您可以从第二个中删除窗口函数 子查询,希望可以使用索引。

阅读范围类型here

Caveat Emptor:除了您提供的微小数据样本之外,我没有对此进行过测试。 我使用类似的东西虽然有一些相似的目的。

相关问题