Опис описує таке визначення таблиці :
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 <> fiddle тут
Стара SQL Fiddle
Покажчик
Найважливішим показником є відповідність індексу (за винятком згаданого процедурного рішення, що працює з одним послідовним скануванням). Такий індекс багатокольонових служб найкраще:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Якщо у вашій таблиці є UNIQUE
обмеження, яке я припускав у верхній частині, воно реалізовано просто з цим (унікальним) індексом всередині, і вам не потрібно створювати інший індекс.