Ваше описание приводит к определению таблицы следующим образом:
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)
);
Общее решение для этого класса проблем
Чтобы получить самую длинную последовательность (1 результат, самый длинный из всех, произвольный выбор, если есть связи):
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)
учитывается только TRUE
(= переход к следующей группе), что приводит к появлению нового номера для каждой новой группы.
Смежный вопрос по SO, один ответ с процедурным решением с помощью plpgsql :
Если главное требование - производительность, функция plpgsql обычно быстрее в этом конкретном случае, потому что она может вычислить результат за одно сканирование.
Быстрее для последовательных номеров
Мы можем извлечь выгоду из того факта, что последовательно lap_no
определяют последовательность, для гораздо более простой и быстрой версии :
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;
Последовательные круги заканчиваются тем же grp
. Каждый пропущенный круг приводит к снижению grp
на раздел.
Это зависит от (race_id, car_type, lap_no)
того, чтобы быть UNIQUE NOT NULL
. Значения NULL или дубликаты могут нарушить логику.
Обсуждение более простой альтернативы Джека
Версия @ Джека эффективно подсчитывает все круги (ряды), где предыдущие lap_no
в этом race_id
были одинаковыми car_type
. Это проще, быстрее и правильнее - при условии, что каждый car_type
может иметь только одну последовательность на race_id
.
Но для такой простой задачи запрос может быть еще проще. Это логически следует , что все lap_no
за (car_type, race_id)
должны быть в последовательности , и мы могли бы просто сосчитать круги:
SELECT race_id, car_type, count(*) AS seq_len
FROM tbl
GROUP BY race_id, car_type
ORDER BY seq_len DESC
LIMIT 1;
Если, с другой стороны, car_type
можно использовать несколько отдельных последовательностей на race_id (и в вопросе не указано иное), версия Джека потерпит неудачу.
Быстрее для данной расы / типа автомобиля
В ответ на комментарий / уточнения в вопросе: ограничение запроса одним заданием , конечно, (race_id, car_type)
сделает его намного быстрее :
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 <> скрипеть здесь
Old SQL Fiddle
Показатель
Ключом к максимальной производительности является соответствующий индекс (за исключением упомянутого процедурного решения, работающего с одним последовательным сканированием). Индекс многоколончатого как это служит лучше всего:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Если ваша таблица имеет UNIQUE
ограничение, которое я принял наверху, то оно реализовано только с помощью этого (уникального) индекса, и вам не нужно создавать другой индекс.
источник
sum((lap_no=(prev+1))::integer)+1
но я не уверен, что это легче читать,