Ihre Beschreibung führt zu einer Tabellendefinition wie dieser:
CREATE TABLE tbl (
lap_id serial PRIMARY KEY
, lap_no int NOT NULL
, car_type enum NOT NULL
, race_id int NOT NULL -- REFERENCES ...
, UNIQUE(race_id, car_type, lap_no)
);
Allgemeine Lösung für diese Klasse von Problemen
Um die längste Sequenz zu erhalten (1 Ergebnis, längste von allen, willkürliche Auswahl bei Gleichstand):
SELECT race_id, car_type, count(*) AS seq_len
FROM (
SELECT *, count(*) FILTER (WHERE step)
OVER (ORDER BY race_id, car_type, lap_no) AS grp
FROM (
SELECT *, (lag(lap_no) OVER (PARTITION BY race_id, car_type ORDER BY lap_no) + 1)
IS DISTINCT FROM lap_no AS step
FROM tbl
) x
) y
GROUP BY race_id, car_type, grp
ORDER BY seq_len DESC
LIMIT 1;
count(*) FILTER (WHERE step)
Es zählt nur TRUE
(= Schritt zur nächsten Gruppe), was für jede neue Gruppe eine neue Nummer ergibt.
Verwandte Frage zu SO, eine Antwort mit einer prozeduralen Lösung mit plpgsql :
Wenn die höchste Anforderung die Leistung ist, ist die Funktion plpgsql in diesem speziellen Fall in der Regel schneller, da sie das Ergebnis in einem einzigen Scan berechnen kann.
Schneller für fortlaufende Nummern
Wir können die Tatsache nutzen, dass fortlaufend lap_no
eine Sequenz definiert wird, um eine viel einfachere und schnellere Version zu erhalten :
SELECT race_id, car_type, count(*) AS seq_len
FROM (
SELECT race_id, car_type
, row_number() OVER (PARTITION BY race_id, car_type ORDER BY lap_no) - lap_no AS grp
FROM tbl
) x
GROUP BY race_id, car_type, grp
ORDER BY seq_len DESC
LIMIT 1;
Aufeinanderfolgende Runden enden im selben grp
. Jede fehlende Runde führt zu einer niedrigeren grp
pro Partition.
Das hängt vom (race_id, car_type, lap_no)
Sein ab UNIQUE NOT NULL
. NULL-Werte oder Duplikate können die Logik zerstören.
Diskussion über Jacks einfachere Alternative
@ Jack-Version effektiv alle Runden (Zeilen) zählt , wo die vorherige lap_no
in dieser race_id
die gleiche hatte car_type
. Das ist einfacher und schneller und korrekter - solange jede Sequenz car_type
nur eine pro haben kann race_id
.
Aber für eine so einfache Aufgabe könnte die Abfrage noch einfacher sein. Es würde logischerweise folgen, dass alle lap_no
pro (car_type, race_id)
der Reihe nach sein müssen , und wir könnten nur die Runden zählen:
SELECT race_id, car_type, count(*) AS seq_len
FROM tbl
GROUP BY race_id, car_type
ORDER BY seq_len DESC
LIMIT 1;
Wenn auf der anderen Seite eine car_type
haben kann mehrere separate Sequenzen pro race_id (und die Frage nichts anderes ergibt), Jack-Version fehl.
Schneller für einen bestimmten Renntyp
Als Antwort auf den Kommentar / die Erläuterungen in der Frage: Wenn Sie die Abfrage auf einen bestimmten (race_id, car_type)
Wert beschränken, wird sie natürlich viel schneller :
SELECT count(*) AS seq_len
FROM (
SELECT row_number() OVER (ORDER BY lap_no) - lap_no AS grp
FROM tbl
WHERE race_id = 1
AND car_type = 'red'
) x
GROUP BY grp
ORDER BY seq_len DESC
LIMIT 1;
db <> hier fummeln
Old SQL Fiddle
Index
Der Schlüssel zur Spitzenleistung ist ein passender Index (mit Ausnahme der erwähnten prozeduralen Lösung, die mit einem einzelnen sequentiellen Scan arbeitet). Ein mehrspaltiger Index wie dieser ist am besten geeignet :
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Wenn Ihre Tabelle UNIQUE
die oben angenommene Einschränkung aufweist, wird diese intern nur mit diesem (eindeutigen) Index implementiert, und Sie müssen keinen weiteren Index erstellen.
quelle
sum((lap_no=(prev+1))::integer)+1
aber ich bin mir nicht sicher, ob das einfacher zu lesen ist