sql >> Database teknologi >  >> RDS >> PostgreSQL

Bestilt antal på hinanden følgende gentagelser / dubletter

Testcase

For det første en mere nyttig måde at præsentere dine data på - eller endnu bedre, i en sqlfiddle , klar til at lege med:

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);

Forenklet forespørgsel

Da det stadig er uklart, antager jeg kun ovenstående som givet.
Dernæst har jeg forenklet din forespørgsel for at nå frem til:

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;

Selvom det hele er flot og skinnende at bruge ren SQL, vil det være meget hurtigere med en plpgsql-funktion, fordi den kan gøre det i en enkelt tabelscanning, hvor denne forespørgsel har brug for mindst tre scanninger.

Hurtigere med plpgsql-funktion:

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$;

Ring til:

SELECT * FROM x.f_repeat_ct();

Sørg for at tabelkvalificere dine kolonnenavne til enhver tid i denne form for plpgsql-funktion, fordi vi bruger de samme navne som outputparametre, som ville have forrang, hvis de ikke er kvalificerede.

Milliarder af rækker

Hvis du har milliarder rækker , vil du måske dele denne operation op. Jeg citerer manualen her:

Bemærk:Den aktuelle implementering af RETURN NEXT og RETURN QUERY gemmer hele resultatsættet, før det vender tilbage fra funktionen, som beskrevet ovenfor. Det betyder, at hvis en PL/pgSQL-funktion producerer et meget stort resultatsæt, kan ydeevnen være dårlig:data vil blive skrevet til disken for at undgå udmattelse af hukommelsen, men selve funktionen vender ikke tilbage, før hele resultatsættet er blevet genereret. En fremtidig version af PL/pgSQL kan tillade brugere at definere sæt-retur-funktioner, der ikke har denne begrænsning. I øjeblikket styres det punkt, hvor data begynder at blive skrevet til disken, af work_memconfiguration-variablen. Administratorer, der har tilstrækkelig hukommelse til at gemme større resultatsæt i hukommelsen, bør overveje at øge denne parameter.

Overvej at beregne rækker for et system ad gangen eller indstil en høj nok værdi for work_mem at klare belastningen. Følg linket i citatet om mere om work_mem.

En måde ville være at indstille en meget høj værdi for work_mem med SET LOCAL i din funktion, som kun er effektiv for den aktuelle transaktion. Jeg tilføjede en kommentarlinje i funktionen. Gør ikke sæt det meget højt globalt, da dette kan ødelægge din server. Læs manualen.




  1. Hvordan kopierer eller importerer jeg Oracle-skemaer mellem to forskellige databaser på forskellige servere?

  2. Test af Android SQLite-databaseenhed

  3. mySQL ::indsætte i tabel, data fra en anden tabel?

  4. Generer et resultatsæt af stigende datoer i TSQL