Postgres 在查询中管理多个一对多关系
Postgres Managing more than one one to many relationship in query
上下文
我正在编写一个对车辆路径问题进行变体处理的应用程序。该应用程序具有路线、停靠点和路线的行车路线。我需要为一个视图编写一个查询,该视图结合了路由的所有相关属性。因此,我需要在单个查询中将路由 table 加入到多个多对多关系中。
查询详情
有路线 table、route_stop_join table 和路线方向 table。路线和站点之间的关系确实是多对多的,但我们只需要一个站点 ID 列表,因此考虑与连接 table 的一对多关系就足够了。以下查询计算总和 n 次,其中 n 是停止次数:
select r.id,
array_agg(j.stop_id) as stops,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
right join routes r
on rd.route_id = r.id
left join routes_stops_join j
on r.id = j.route_id
group by r.id;
我可以使用这样的子选择来做到这一点:
select rj.id,
rj.stops,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
right join (select r.id,
array_agg(j.stop_id) as stops
from routes r
left join routes_stops_join j
on r.id = j.route_id
group by r.id) rj
on rj.id = rd.route_id
group by rj.id, rj.stops;
但我想看看是否有一种方法可以在没有子选择的情况下在单个查询中执行此操作。
至此查询returns 99%你需要的信息:
select rd.id,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
group by rd.id;
我建议使用子查询或 CTE,但使用 LEFT JOIN 而不是 RIGHT JOIN。
create table routes(id int);
insert into routes values (1),(2);
create table routes_stops(route_id int, stop_id int);
insert into routes_stops values (1,1),(1,2),(2,1),(2,3),(2,4);
create table routes_directions(route_id int, dir_id int, time_elapsed int, drive_distance int);
insert into routes_directions values (1,1,100,40),(1,2,60,60),(2,1,15,14),(2,3,20,30);
select rj.id,
rj.stops,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
left join (select r.id,
array_agg(j.stop_id) as stops
from routes r
left join routes_stops j
on r.id = j.route_id
group by r.id) rj
on rj.id = rd.route_id
group by rj.id, rj.stops;
id | stops | total_time | total_distance
-: | :------ | ---------: | -------------:
2 | {1,3,4} | 35 | 44
1 | {1,2} | 160 | 100
with stp as
(
select r.id,
array_agg(j.stop_id) as stops
from routes r
left join routes_stops j
on r.id = j.route_id
group by r.id
)
select rd.route_id,
stp.stops,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
left join stp
on stp.id = rd.route_id
group by rd.route_id, stp.stops;
route_id | stops | total_time | total_distance
-------: | :------ | ---------: | -------------:
1 | {1,2} | 160 | 100
2 | {1,3,4} | 35 | 44
dbfiddle here
上下文
我正在编写一个对车辆路径问题进行变体处理的应用程序。该应用程序具有路线、停靠点和路线的行车路线。我需要为一个视图编写一个查询,该视图结合了路由的所有相关属性。因此,我需要在单个查询中将路由 table 加入到多个多对多关系中。
查询详情
有路线 table、route_stop_join table 和路线方向 table。路线和站点之间的关系确实是多对多的,但我们只需要一个站点 ID 列表,因此考虑与连接 table 的一对多关系就足够了。以下查询计算总和 n 次,其中 n 是停止次数:
select r.id,
array_agg(j.stop_id) as stops,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
right join routes r
on rd.route_id = r.id
left join routes_stops_join j
on r.id = j.route_id
group by r.id;
我可以使用这样的子选择来做到这一点:
select rj.id,
rj.stops,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
right join (select r.id,
array_agg(j.stop_id) as stops
from routes r
left join routes_stops_join j
on r.id = j.route_id
group by r.id) rj
on rj.id = rd.route_id
group by rj.id, rj.stops;
但我想看看是否有一种方法可以在没有子选择的情况下在单个查询中执行此操作。
至此查询returns 99%你需要的信息:
select rd.id,
sum(rd.time_elapsed) as total_time,
sum(rd.drive_distance) as total_distance
from routes_directions rd
group by rd.id;
我建议使用子查询或 CTE,但使用 LEFT JOIN 而不是 RIGHT JOIN。
create table routes(id int); insert into routes values (1),(2); create table routes_stops(route_id int, stop_id int); insert into routes_stops values (1,1),(1,2),(2,1),(2,3),(2,4); create table routes_directions(route_id int, dir_id int, time_elapsed int, drive_distance int); insert into routes_directions values (1,1,100,40),(1,2,60,60),(2,1,15,14),(2,3,20,30);
select rj.id, rj.stops, sum(rd.time_elapsed) as total_time, sum(rd.drive_distance) as total_distance from routes_directions rd left join (select r.id, array_agg(j.stop_id) as stops from routes r left join routes_stops j on r.id = j.route_id group by r.id) rj on rj.id = rd.route_id group by rj.id, rj.stops;
id | stops | total_time | total_distance -: | :------ | ---------: | -------------: 2 | {1,3,4} | 35 | 44 1 | {1,2} | 160 | 100
with stp as ( select r.id, array_agg(j.stop_id) as stops from routes r left join routes_stops j on r.id = j.route_id group by r.id ) select rd.route_id, stp.stops, sum(rd.time_elapsed) as total_time, sum(rd.drive_distance) as total_distance from routes_directions rd left join stp on stp.id = rd.route_id group by rd.route_id, stp.stops;
route_id | stops | total_time | total_distance -------: | :------ | ---------: | -------------: 1 | {1,2} | 160 | 100 2 | {1,3,4} | 35 | 44
dbfiddle here