PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Bestellte Anzahl aufeinanderfolgender Wiederholungen / Duplikate

Testfall

Erstens eine nützlichere Möglichkeit, Ihre Daten zu präsentieren - oder noch besser, in einem sqliddle , spielbereit mit:

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

Vereinfachte Abfrage

Da es unklar bleibt, nehme ich nur das Obige als gegeben an.
Als nächstes habe ich Ihre Abfrage vereinfacht, um zu folgendem zu gelangen:

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;

Nun, obwohl es schön und glänzend ist, reines SQL zu verwenden, wird dies viel sein schneller mit einer plpgsql-Funktion, da sie dies in einem einzigen Tabellenscan tun kann, wo diese Abfrage mindestens drei Scans benötigt.

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

Aufruf:

SELECT * FROM x.f_repeat_ct();

Stellen Sie sicher, dass Sie Ihre Spaltennamen in dieser Art von plpgsql-Funktion immer tabellarisch qualifizieren, da wir die gleichen Namen als Ausgabeparameter verwenden, die Vorrang hätten, wenn sie nicht qualifiziert wären.

Milliarden Zeilen

Wenn Sie Milliarden haben von Reihen , möchten Sie diesen Vorgang möglicherweise aufteilen. Ich zitiere das Handbuch hier:

Hinweis:Die aktuelle Implementierung von RETURN NEXT und RETURN QUERY speichert die gesamte Ergebnismenge, bevor sie von der Funktion zurückkehrt, wie oben besprochen. Das bedeutet, wenn eine PL/pgSQL-Funktion eine sehr große Ergebnismenge erzeugt, kann die Leistung schlecht sein:Daten werden auf die Festplatte geschrieben, um eine Erschöpfung des Speichers zu vermeiden, aber die Funktion selbst wird erst zurückkehren, wenn die gesamte Ergebnismenge generiert wurde. Eine zukünftige Version von PL/pgSQL könnte es Benutzern ermöglichen, mengenzurückgebende Funktionen zu definieren, die diese Einschränkung nicht haben. Derzeit wird der Punkt, an dem mit dem Schreiben von Daten auf die Platte begonnen wird, von der work_memconfiguration-Variablen gesteuert. Administratoren, die über ausreichend Speicher verfügen, um größere Ergebnismengen im Speicher zu speichern, sollten diesen Parameter erhöhen.

Erwägen Sie, Zeilen für jeweils ein System zu berechnen, oder legen Sie einen ausreichend hohen Wert für work_mem fest um die Belastung zu bewältigen. Folgen Sie dem im Zitat angegebenen Link, um mehr über work_mem zu erfahren.

Eine Möglichkeit wäre, einen sehr hohen Wert für work_mem festzulegen mit SET LOCAL in Ihrer Funktion, die nur für die aktuelle Transaktion wirksam ist. Ich habe eine kommentierte Zeile in der Funktion hinzugefügt. nicht Stellen Sie es global sehr hoch ein, da dies Ihren Server zerstören könnte. Lesen Sie das Handbuch.