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.