我不认为您可以使用普通查询、CTE 和窗口函数廉价地做到这一点——它们的框架定义是静态的,但您需要一个动态框架(取决于列值)。
通常,您必须仔细定义窗口的下限和上限:以下查询排除当前行并包含下边框。
仍然有一个细微的区别:该函数包括当前行的先前对等点,而相关子查询将它们排除在外...
测试用例
使用ts
而不是保留字date
作为列名。
CREATE TABLE test (
id bigint
, ts timestamp
);
使用 CTE,将时间戳聚合到一个数组中,unnest,计数......
虽然正确,但性能会急剧下降,而不是一手满满的行。这里有几个性能杀手。见下文。
ARR - 计算数组元素
我接受了 Roman 的查询并尝试对其进行简化:
- 删除不需要的第二个 CTE。
- 将第一个 CTE 转换为子查询,这样更快。
- 直接
count()
而不是重新聚合到数组中并使用array_length()
.
但是数组处理成本很高,而且随着行的增加,性能仍然会严重下降。
SELECT id, ts
, (SELECT count(*)::int - 1
FROM unnest(dates) x
WHERE x >= sub.ts - interval '1h') AS ct
FROM (
SELECT id, ts
, array_agg(ts) OVER(ORDER BY ts) AS dates
FROM test
) sub;
COR - 相关子查询
您可以使用简单的相关子查询来解决它。快了很多,但仍然...
SELECT id, ts
, (SELECT count(*)
FROM test t1
WHERE t1.ts >= t.ts - interval '1h'
AND t1.ts < t.ts) AS ct
FROM test t
ORDER BY ts;
FNC - 功能
row_number()
使用plpgsql 函数按时间顺序循环行,并将其与同一查询上的游标结合,跨越所需的时间范围。然后我们可以减去行号:
CREATE OR REPLACE FUNCTION running_window_ct(_intv interval = '1 hour')
RETURNS TABLE (id bigint, ts timestamp, ct int)
LANGUAGE plpgsql AS
$func$
DECLARE
cur CURSOR FOR
SELECT t.ts + _intv AS ts1, row_number() OVER (ORDER BY t.ts) AS rn
FROM test t ORDER BY t.ts;
rec record;
rn int;
BEGIN
OPEN cur;
FETCH cur INTO rec;
ct := -1; -- init
FOR id, ts, rn IN
SELECT t.id, t.ts, row_number() OVER (ORDER BY t.ts)
FROM test t ORDER BY t.ts
LOOP
IF rec.ts1 >= ts THEN
ct := ct + 1;
ELSE
LOOP
FETCH cur INTO rec;
EXIT WHEN rec.ts1 >= ts;
END LOOP;
ct := rn - rec.rn;
END IF;
RETURN NEXT;
END LOOP;
END
$func$;
以一小时的默认间隔调用:
SELECT * FROM running_window_ct();
或任何间隔:
SELECT * FROM running_window_ct('2 hour - 3 second');
db<>fiddle here
旧sqlfiddle
基准
使用上面的表格,我在我的旧测试服务器上运行了一个快速基准测试:(Debian 上的 PostgreSQL 9.1.9)。
-- TRUNCATE test;
INSERT INTO test
SELECT g, '2013-08-08'::timestamp
+ g * interval '5 min'
+ random() * 300 * interval '1 min' -- halfway realistic values
FROM generate_series(1, 10000) g;
CREATE INDEX test_ts_idx ON test (ts);
ANALYZE test; -- temp table needs manual analyze
我为每次运行改变了粗体EXPLAIN ANALYZE
部分,并在 5 个中取了最好的部分。
100 行
ROM:27.656 毫秒
ARR:7.834 毫秒
COR:5.488 毫秒
FNC:1.115 毫秒
1000 行
ROM:2116.029 毫秒
ARR:189.679 毫秒
COR:65.802 毫秒
FNC:8.466 毫秒
5000 行
ROM:51347 毫秒!
ARR:3167 毫秒
COR:333 毫秒
FNC:42 毫秒
100000 行
ROM:DNF
ARR:DNF
COR:6760 毫秒
FNC:828 毫秒
功能是明显的胜利者。它是最快的一个数量级并且规模最好。
数组处理无法竞争。