Votre description donne une définition de table comme celle-ci:
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)
);
Solution générale pour cette classe de problèmes
Pour obtenir la séquence la plus longue (1 résultat, le plus long de tous, choix arbitraire s'il y a des liens):
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)
ne compte que TRUE
(= pas au groupe suivant), ce qui donne un nouveau numéro pour chaque nouveau groupe.
Question connexe sur SO, une réponse présentant une solution procédurale avec plpgsql :
Si la principale exigence est la performance, la fonction plpgsql est généralement plus rapide dans ce cas particulier car elle peut calculer le résultat en une seule analyse.
Plus rapide pour les numéros consécutifs
On peut capitaliser sur le fait que consécutivement lap_no
définir une séquence, pour une version beaucoup plus simple et plus rapide :
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;
Des tours consécutifs finissent dans le même grp
. Chaque tour manquant entraîne une baisse grp
par partition.
Cela repose sur l' (race_id, car_type, lap_no)
être UNIQUE NOT NULL
. Les valeurs NULL ou les doublons peuvent briser la logique.
Discussion sur l'alternative plus simple de Jack
@ Version de Jack compte efficacement tous les tours (lignes) où le précédent lap_no
dans ce race_id
avaient le même car_type
. C'est plus simple, plus rapide et correct - tant que chacun car_type
ne peut avoir qu'une seule séquence par race_id
.
Mais pour une tâche aussi simple, la requête pourrait être encore plus simple. Il serait logique que tous les suivre lap_no
par (car_type, race_id)
doit être dans l' ordre , et nous pouvions compter les tours:
SELECT race_id, car_type, count(*) AS seq_len
FROM tbl
GROUP BY race_id, car_type
ORDER BY seq_len DESC
LIMIT 1;
Si, d'autre part, on car_type
peut avoir plusieurs séquences distinctes par race_id (et la question ne spécifie pas le contraire), la version de Jack échouera.
Plus rapide pour un type de course / voiture donné
En réponse aux commentaires / clarifications de la question: restreindre la requête à une donnée la (race_id, car_type)
rendra beaucoup plus rapide , bien sûr:
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 <> violon ici
Old SQL Fiddle
Indice
La clé des meilleures performances est un index approprié (sauf pour la solution procédurale mentionnée fonctionnant avec un seul balayage séquentiel). Un index multicolonne comme celui-ci sert mieux:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Si votre table a la UNIQUE
contrainte que j'ai supposée en haut, celle-ci est implémentée avec uniquement cet index (unique) en interne, et vous n'avez pas besoin de créer un autre index.
la source
sum((lap_no=(prev+1))::integer)+1
mais je ne suis pas sûr que ce soit plus facile à lire