我们有很多人,这些人带着多个阶段/州进行旅行(最初计划,然后开始,然后返回_安全或在灾难中结束).
我有一个查询可以得到正确的结果,你可以在这里看到并使用它:
但是,我想知道是否有更好的实现,特别是避免使用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');
Run Code Online (Sandbox Code Playgroud)
person_name | gender | age
-------------+--------+-----
jess | f | 24
agnes | f | 25
Run Code Online (Sandbox Code Playgroud)
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')
)
Run Code Online (Sandbox Code Playgroud)
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')
)
Run Code Online (Sandbox Code Playgroud)
还有其他方法可以编写此查询吗?没有使用GROUP BY
和bool_and
理想情况下不使用子查询?也许一些分区/窗口功能?
我正在用它来学习,所以对查询的解释/分析表示赞赏!
我对性能影响特别感兴趣.例如,如果人们进行数千次旅行会发生什么?子查询是否通过其他方法执行?
您可以使用“不”not exists
来选择没有至少一次旅行但未安全返回的所有人员(这意味着他们要么没有进行任何旅行,要么从所有旅行中安全返回)并且没有至少一次计划的旅行这不在一个阶段
select * from people p
where not exists (
select 1 from trips t
left join trip_stages ts on ts.trip_name = t.trip_name
where ((ts.stage <> 'safe_return' -- did not end in safe return
and ts.most_recent = 't')
or ts.trip_name is null) -- or does not have a trip stage
and t.person_name = p.person_name
)
Run Code Online (Sandbox Code Playgroud)
http://sqlfiddle.com/#!15/3416a/18