sql >> Databáze >  >> RDS >> PostgreSQL

Uspořádaný počet po sobě jdoucích opakování / duplikátů

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 a RETURN 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.




  1. Jak porovnat data v polích datetime v Postgresql?

  2. Konektor Access Dataverse je nyní k dispozici k testování

  3. Nelze vrátit výsledky z uložené procedury pomocí kurzoru Pythonu

  4. SQL Server Vysvětlení libovolného operátora