Su descripción da como resultado una definición de tabla como esta:
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)
);
Solución general para esta clase de problemas.
Para obtener la secuencia más larga (1 resultado, el más largo de todos, selección arbitraria si hay empates):
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)
solo cuenta TRUE
(= paso al siguiente grupo), lo que da como resultado un nuevo número para cada nuevo grupo.
Pregunta relacionada sobre SO, una respuesta con una solución de procedimiento con plpgsql :
Si el requisito principal es el rendimiento, la función plpgsql suele ser más rápida en este caso particular porque puede calcular el resultado en un solo escaneo.
Más rápido para números consecutivos
Podemos aprovechar el hecho de que una secuencia consecutiva lap_no
define una secuencia mucho más simple y rápida :
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;
Vueltas consecutivas terminan en el mismo grp
. Cada vuelta que falta resulta en una baja grp
por partición.
Esto se basa en el (race_id, car_type, lap_no)
ser UNIQUE NOT NULL
. Los valores NULL o duplicados podrían romper la lógica.
Discusión de la alternativa más simple de Jack
La versión de @ Jack cuenta de manera efectiva todas las vueltas (filas) donde la anterior lap_no
en esto race_id
tenía lo mismo car_type
. Eso es más simple, más rápido y correcto, siempre y cuando cada uno car_type
solo pueda tener una secuencia por race_id
.
Pero para una tarea así de simple, la consulta podría ser aún más simple. De ello se sigue lógicamente que todo lap_no
por (car_type, race_id)
debe ser de forma secuencial , y que sólo podía contar las vueltas:
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, por otro lado, uno car_type
puede tener múltiples secuencias separadas por race_id (y la pregunta no especifica lo contrario), la versión de Jack fallará.
Más rápido para una carrera / tipo de auto determinado
En respuesta al comentario / aclaraciones en la pregunta: restringir la consulta a una dada (race_id, car_type)
lo hará mucho más rápido , por supuesto:
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 <> violín aquí
Viejo violín de SQL
Índice
La clave para lograr un rendimiento superior es un índice de ajuste (a excepción de la solución de procedimiento mencionada que funciona con un solo escaneo secuencial). Un índice de varias columnas como este sirve mejor:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Si su tabla tiene la UNIQUE
restricción que asumí en la parte superior, eso se implementa solo con este índice (único) internamente, y no necesita crear otro índice.
fuente
sum((lap_no=(prev+1))::integer)+1
pero no estoy seguro de que sea más fácil de leer