demo: db <> fiddle
SELECT parcel, passed_scanners
FROM (
SELECT
parcel,
array_agg(scanner) as passed_scanners
FROM
scanners
GROUP BY parcel
)s
WHERE 's15' = ANY(passed_scanners) AND NOT (ARRAY['s71', 's72'] && passed_scanners)
- Объединение всех пройденных
scanner_id
s для каждой посылки - Фильтрация всех строк участков, содержащих #15 в
passed_scanners
, но ни # 71, ни # 72 (оператор &&
для двух массивов проверяет, содержатся ли элементы массива в обоих массивах - так называемое перекрытие массива )
Вторая часть - это другой запрос, потому что в первой вы запрашиваете посылки, которые еще не дошли до последнего сканера.Во второй части вы хотите вычислить продолжительность для всех посылок, которые наконец достигли s71.
SELECT parcel, duration
FROM (
SELECT
parcel,
MAX(time) FILTER (WHERE scanner IN ('s71', 's72')) -
MIN(time) FILTER (WHERE scanner = 's15') AS duration,
array_agg(scanner) as passed_scanners
FROM
scanners
GROUP BY parcel
)s
WHERE 's15' = ANY(passed_scanners) AND (ARRAY['s71', 's72'] && passed_scanners)
- Снова агрегируйте все переданные scanner_ids для каждой посылки
- Выдайте первый раз длясканер 15 и последний раз для сканера 71 или 72. Постройте разницу, чтобы получить длительность.
- Отфильтруйте все строки участков, содержащие # 15 в
passed_scanners
и либо # 71, либо # 72
Обратите внимание : Без какого-либо компонента даты в столбце time
результат для продолжительности может не работать, если посылка сканируется за полночь.Тогда ваш старт на s15 может быть в 23 часа, а пункт назначения - в 1 час.Разница во времени будет (1 - 23) = -22
.Чтобы решить эту проблему, я настоятельно рекомендую сохранить всю метку времени с частью даты.
В противном случае вам нужно проверить: Если duration < 0
, то добавьте 24 часа (но что, если посылке требуется больше, чем 24 часа? Как узнатьчто вам не нужно добавлять 48 часов?)