Сценарий
У нас много людей, эти люди отправляются в путешествие с несколькими этапами/состояниями (первоначально планировалось, затем начиналось, потом возвращалось или становилось катастрофой).
У меня есть запрос, который дает правильные результаты, вы можете видеть его и играть с ним здесь:
Однако мне интересно, есть ли более эффективная реализация, в частности, избегая использования GROUP BY
и postgres 'bool_and
, потенциально также избегая вложенного запроса.
Что мы хотим знать
Кто никогда не испытывал поездки, от которой они не возвращались безопасно?
Или, по-другому:
Кто имеет:
1. Never planned or gone on a trip
ИЛИ2. only ever returned safely
Разъяснения
- Если есть запись для человека в таблице поездок, но нет этапов, они планируют поездку.
Выход
Должно быть по крайней мере все столбцы из таблицы person
, если выходят и другие столбцы, это прекрасно.
Настройка/воспроизведение
CREATE TABLE people (person_name text, gender text, age integer);
INSERT INTO people (person_name, gender, age)
VALUES ('pete', 'm', 10), ('alan', 'm', 22), ('jess', 'f', 24), ('agnes', 'f', 25), ('matt', 'm', 26);
CREATE TABLE trips (person_name text, trip_name text);
INSERT INTO trips (person_name, trip_name)
VALUES ('pete', 'a'),
('pete', 'b'),
('alan', 'c'),
('alan', 'd'),
('jess', 'e'),
('matt', 'f');
CREATE TABLE trip_stages (trip_name text, stage text, most_recent boolean);
INSERT INTO trip_stages
VALUES ('a', 'started', 'f'), ('a', 'disaster', 't'),
('b', 'started', 't'),
('c', 'started', 'f'), ('c', 'safe_return', 't'),
('e', 'started', 'f'), ('e', 'safe_return', 't');
Краткое описание ситуации
- У Пита одна поездка, которая закончилась катастрофой, и он только что начал
- У Алана есть одна поездка, которую он вернул из безопасности, и тот, который он планирует
- Джесс была в одной поездке, которую она благополучно вернулась из
- Агнес никогда даже не планировала поездку
- Мэтт запланировал поездку, но еще не начал ее
Решение
person_name | gender | age
-------------+--------+-----
jess | f | 24
agnes | f | 25
- Джесс (была в одной поездке, с которой она благополучно вернулась)
- Агнес (никогда не планировал поездку)
Рабочий запрос
SELECT people.* FROM people WHERE people.person_name IN (
SELECT people.person_name FROM people
LEFT OUTER JOIN trips
ON trips.person_name = people.person_name
LEFT OUTER JOIN trip_stages
ON trip_stages.trip_name = trips.trip_name AND trip_stages.most_recent = 't'
GROUP BY people.person_name
HAVING bool_and(trips.trip_name IS NULL)
OR bool_and(trip_stages.stage IS NOT NULL AND trip_stages.stage = 'safe_return')
)
Объяснение
SELECT people.* FROM people WHERE people.person_name IN (
-- All the people
SELECT people.person_name FROM people
-- + All their trips
LEFT OUTER JOIN trips
ON trips.person_name = people.person_name
-- + All those trips' stages
LEFT OUTER JOIN trip_stages
ON trip_stages.trip_name = trips.trip_name AND trip_stages.most_recent = 't'
-- Group by person
GROUP BY people.person_name
-- Filter to those rows where either:
-- 1. trip_name is always NULL (they've made no trips)
-- 2. Every trip has been ended with a safe return
HAVING bool_and(trips.trip_name IS NULL)
OR bool_and(trip_stages.stage IS NOT NULL AND trip_stages.stage = 'safe_return')
)
Вопрос
Есть ли другой способ написать этот запрос? Без использования GROUP BY
и bool_and
и в идеале без использования подзапросов тоже? Возможно, какая-либо функция раздела/окна?
Я использую это, чтобы узнать, поэтому объяснения/анализ запросов оцениваются!
Меня особенно интересуют последствия для производительности. например Что произойдет, если люди совершают тысячи поездок? Выбирают ли подзапросы какой-то другой подход?