Testovací případ
Za prvé, užitečnější způsob prezentace dat – nebo ještě lépe v sqlfiddle , připraven k hraní:
CREATE TEMP TABLE data(
system_measured int
, time_of_measurement int
, measurement int
);
INSERT INTO data VALUES
(1, 1, 5)
,(1, 2, 150)
,(1, 3, 5)
,(1, 4, 5)
,(2, 1, 5)
,(2, 2, 5)
,(2, 3, 5)
,(2, 4, 5)
,(2, 5, 150)
,(2, 6, 5)
,(2, 7, 5)
,(2, 8, 5);
Zjednodušený dotaz
Protože to zůstává nejasné, předpokládám pouze výše uvedené, jak je uvedeno.
Dále jsem váš dotaz zjednodušil tak, aby se dostal na:
WITH x AS (
SELECT *, CASE WHEN lag(measurement) OVER (PARTITION BY system_measured
ORDER BY time_of_measurement) = measurement
THEN 0 ELSE 1 END AS step
FROM data
)
, y AS (
SELECT *, sum(step) OVER(PARTITION BY system_measured
ORDER BY time_of_measurement) AS grp
FROM x
)
SELECT * ,row_number() OVER (PARTITION BY system_measured, grp
ORDER BY time_of_measurement) - 1 AS repeat_ct
FROM y
ORDER BY system_measured, time_of_measurement;
Nyní, i když je vše hezké a nablýskané používat čisté SQL, bude to moc rychlejší s funkcí plpgsql, protože to dokáže v rámci skenování jedné tabulky, kde tento dotaz vyžaduje alespoň tři skeny.
Rychlejší s funkcí plpgsql:
CREATE OR REPLACE FUNCTION x.f_repeat_ct()
RETURNS TABLE (
system_measured int
, time_of_measurement int
, measurement int, repeat_ct int
) LANGUAGE plpgsql AS
$func$
DECLARE
r data; -- table name serves as record type
r0 data;
BEGIN
-- SET LOCAL work_mem = '1000 MB'; -- uncomment an adapt if needed, see below!
repeat_ct := 0; -- init
FOR r IN
SELECT * FROM data d ORDER BY d.system_measured, d.time_of_measurement
LOOP
IF r.system_measured = r0.system_measured
AND r.measurement = r0.measurement THEN
repeat_ct := repeat_ct + 1; -- start new array
ELSE
repeat_ct := 0; -- start new count
END IF;
RETURN QUERY SELECT r.*, repeat_ct;
r0 := r; -- remember last row
END LOOP;
END
$func$;
Volejte:
SELECT * FROM x.f_repeat_ct();
V tomto druhu funkce plpgsql nezapomeňte vždy kvalifikovat názvy sloupců podle tabulky, protože používáme stejné názvy jako výstupní parametry, které by měly přednost, pokud by nebyly kvalifikovány.
Miliardy řádků
Pokud máte miliardy řádků , možná budete chtít tuto operaci rozdělit. Zde cituji manuál:
Poznámka:Aktuální implementace
RETURN NEXT
aRETURN QUERY
ukládá celou sadu výsledků před návratem z funkce, jak je popsáno výše. To znamená, že pokud funkce PL/pgSQL produkuje velmi velkou sadu výsledků, výkon může být špatný:data budou zapsána na disk, aby se předešlo vyčerpání paměti, ale funkce samotná se nevrátí, dokud nebude vygenerována celá sada výsledků. Budoucí verze PL/pgSQL může uživatelům umožnit definovat funkce vrácení sady, které toto omezení nemají. V současné době je bod, kdy se data začínají zapisovat na disk, řízen proměnnou work_memconfiguration. Správci, kteří mají dostatek paměti k ukládání větších sad výsledků do paměti, by měli zvážit zvýšení tohoto parametru.
Zvažte výpočet řádků pro jeden systém najednou nebo nastavte dostatečně vysokou hodnotu pro work_mem
vyrovnat se se zátěží. Klikněte na odkaz uvedený v citaci o více o work_mem.
Jedním ze způsobů by bylo nastavit velmi vysokou hodnotu pro work_mem
pomocí SET LOCAL
ve vaší funkci, která je účinná pouze pro aktuální transakci. Do funkce jsem přidal komentovaný řádek. Ne nastavte ji globálně velmi vysoko, protože by to mohlo zničit váš server. Přečtěte si příručku.