Rekursywny CTE
Ponieważ każdy wiersz zależy od poprzedniego, trudno jest go rozwiązać za pomocą podejścia opartego na zbiorach. Korzystanie z rekurencyjnego CTE (co jest standardowym SQL):
WITH RECURSIVE cte AS (
(SELECT ts FROM tbl
ORDER BY ts
LIMIT 1)
UNION ALL
(SELECT t.ts
FROM cte c
JOIN tbl t ON t.ts >= c.ts + interval '5 min'
ORDER BY t.ts
LIMIT 1)
)
SELECT * FROM cte ORDER BY ts;
Zwróć uwagę na aktualizację z mojego pierwszego szkicu:
Funkcje agregujące nie są dozwolone w rekurencyjnym CTE. Zastąpiłem ORDER BY
/ LIMIT 1
, który powinien być szybki, gdy jest wspierany przez indeks na ts
.
Nawiasy wokół każdej nogi UNION
zapytanie jest konieczne, aby zezwolić na LIMIT
, co w przeciwnym razie byłoby dozwolone tylko raz na końcu UNION
zapytanie.
Funkcja PL/pgSQL
Rozwiązanie proceduralne (przykład z funkcją plpgsql) iterujące po posortowanej tabeli byłoby prawdopodobnie znacznie szybsze, ponieważ może wystarczyć skanowanie pojedynczej tabeli:
CREATE OR REPLACE FUNCTION f_rowgrid(i interval)
RETURNS SETOF timestamp AS
$func$
DECLARE
_this timestamp;
_last timestamp := '-infinity'; -- init so that 1 row passes
BEGIN
FOR _this IN
SELECT ts FROM tbl ORDER BY 1
LOOP
IF _this >= _last + i THEN
RETURN NEXT _this;
_last := _this;
END IF;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Zadzwoń:
SELECT * FROM f_rowgrid('5 min')
Skrzypce SQL demonstrując oba.
Oto nieco bardziej złożony przykład tego typu funkcji plpgsql:
Można łatwo uczynić generycznym za pomocą dynamicznego SQL i EXECUTE
pracować dla dowolnych tabel.