La tua descrizione risulta in una definizione di tabella come questa:
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)
);
Soluzione generale per questa classe di problemi
Per ottenere la sequenza più lunga (1 risultato, il più lungo di tutti, scelta arbitraria in caso di legami):
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)
conta solo TRUE
(= passaggio al gruppo successivo), che risulta in un nuovo numero per ogni nuovo gruppo.
Domanda correlata su SO, una risposta con una soluzione procedurale con plpgsql :
Se il requisito principale è la prestazione, la funzione plpgsql è in genere più veloce in questo caso particolare perché può calcolare il risultato in una singola scansione.
Più veloce per numeri consecutivi
Possiamo capitalizzare sul fatto che consecutivamente lap_no
definire una sequenza, per una versione molto più semplice e veloce :
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;
I giri consecutivi finiscono nello stesso grp
. Ogni giro mancante comporta una riduzione grp
per partizione.
Questo si basa (race_id, car_type, lap_no)
sull'essere UNIQUE NOT NULL
. Valori NULL o duplicati potrebbero violare la logica.
Discussione sull'alternativa più semplice di Jack
La versione di @ Jack conta effettivamente tutti i giri (righe) in cui il precedente lap_no
in questo race_id
aveva lo stesso car_type
. È più semplice, veloce e corretto, purché ciascuno car_type
possa avere solo una sequenza per race_id
.
Ma per un'attività così semplice la query potrebbe essere ancora più semplice. Seguirebbe logicamente che tutto lap_no
per (car_type, race_id)
deve essere in sequenza e potremmo semplicemente contare i giri:
SELECT race_id, car_type, count(*) AS seq_len
FROM tbl
GROUP BY race_id, car_type
ORDER BY seq_len DESC
LIMIT 1;
Se, d'altra parte, si car_type
possono avere più sequenze separate per race_id (e la domanda non specifica diversamente), la versione di Jack fallirà.
Più veloce per un determinato tipo di gara / auto
In risposta al commento / chiarimenti nella domanda: limitare la query a un dato (race_id, car_type)
renderà molto più veloce , ovviamente:
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 <> violino qui
Vecchio SQL Fiddle
Indice
La chiave per le massime prestazioni è un indice adeguato (ad eccezione della soluzione procedurale menzionata che funziona con una singola scansione sequenziale). Un indice a più colonne come questo serve al meglio:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Se la tua tabella ha il UNIQUE
vincolo che ho assunto in alto, questo è implementato con questo indice (unico) internamente e non è necessario creare un altro indice.