[英]Recursive/Hierarchical Query Using Postgres
表: Flight (flight_num, src_city, dest_city, dep_time, arr_time, airfare, mileage)
我需要找到从任何给定的源市到任何给定目的地城市的无限制停靠的最便宜的票价。 问题在于,这可能涉及多次飞行 ,例如,如果我从蒙特利尔 - >堪萨斯城飞行,我可以从蒙特利尔 - >华盛顿,然后从华盛顿 - >堪萨斯城等等。 我将如何使用Postgres查询生成此内容?
样本数据:
create table flight(
flight_num BIGSERIAL PRIMARY KEY,
source_city varchar,
dest_city varchar,
dep_time int,
arr_time int,
airfare int,
mileage int
);
insert into flight VALUES
(101, 'Montreal', 'NY', 0530, 0645, 180, 170),
(102, 'Montreal', 'Washington', 0100, 0235, 100, 180),
(103, 'NY', 'Chicago', 0800, 1000, 150, 300),
(105, 'Washington', 'KansasCity', 0600, 0845, 200, 600),
(106, 'Washington', 'NY', 1200, 1330, 50, 80),
(107, 'Chicago', 'SLC', 1100, 1430, 220, 750),
(110, 'KansasCity', 'Denver', 1400, 1525, 180, 300),
(111, 'KansasCity', 'SLC', 1300, 1530, 200, 500),
(112, 'SLC', 'SanFran', 1800, 1930, 85, 210),
(113, 'SLC', 'LA', 1730, 1900, 185, 230),
(115, 'Denver', 'SLC', 1500, 1600, 75, 300),
(116, 'SanFran', 'LA', 2200, 2230, 50, 75),
(118, 'LA', 'Seattle', 2000, 2100, 150, 450);
[这个答案基于戈登的]
我将arr_time和dep_time更改为TIME
数据类型,这使计算更容易。 还为total_time和waiting_time添加了结果列。 注意 :如果图中可能存在任何循环,则需要避免它们(可能使用数组来存储路径)
WITH RECURSIVE segs AS (
SELECT f0.flight_num::text as flight
, src_city, dest_city
, dep_time AS departure
, arr_time AS arrival
, airfare, mileage
, 1 as hops
, (arr_time - dep_time)::interval AS total_time
, '00:00'::interval as waiting_time
FROM flight f0
WHERE src_city = 'SLC' -- <SRC_CITY>
UNION ALL
SELECT s.flight || '-->' || f1.flight_num::text as flight
, s.src_city, f1.dest_city
, s.departure AS departure
, f1.arr_time AS arrival
, s.airfare + f1.airfare as airfare
, s.mileage + f1.mileage as mileage
, s.hops + 1 AS hops
, s.total_time + (f1.arr_time - f1.dep_time)::interval AS total_time
, s.waiting_time + (f1.dep_time - s.arrival)::interval AS waiting_time
FROM segs s
JOIN flight f1
ON f1.src_city = s.dest_city
AND f1.dep_time > s.arrival -- you can't leave until you are there
)
SELECT *
FROM segs
WHERE dest_city = 'LA' -- <DEST_CITY>
ORDER BY airfare desc
;
仅供参考:表格结构的变化:
create table flight
( flight_num BIGSERIAL PRIMARY KEY
, src_city varchar
, dest_city varchar
, dep_time TIME
, arr_time TIME
, airfare INTEGER
, mileage INTEGER
);
而对于数据:
insert into flight VALUES
(101, 'Montreal', 'NY', '05:30', '06:45', 180, 170),
(102, 'Montreal', 'Washington', '01:00', '02:35', 100, 180),
(103, 'NY', 'Chicago', '08:00', '10:00', 150, 300),
(105, 'Washington', 'KansasCity', '06:00', '08:45', 200, 600),
(106, 'Washington', 'NY', '12:00', '13:30', 50, 80),
(107, 'Chicago', 'SLC', '11:00', '14:30', 220, 750),
(110, 'KansasCity', 'Denver', '14:00', '15:25', 180, 300),
(111, 'KansasCity', 'SLC', '13:00', '15:30', 200, 500),
(112, 'SLC', 'SanFran', '18:00', '19:30', 85, 210),
(113, 'SLC', 'LA', '17:30', '19:00', 185, 230),
(115, 'Denver', 'SLC', '15:00', '16:00', 75, 300),
(116, 'SanFran', 'LA', '22:00', '22:30', 50, 75),
(118, 'LA', 'Seattle', '20:00', '21:00', 150, 450);
您想为此使用递归CTE。 但是,您必须决定要包含多少航班。 以下(未经测试的)查询显示了如何执行此操作,将航段数限制为5:
with recursive segs as (
select cast(f.flight_num as varchar(255)) as flight, src_city, dest_city, dept_time,
arr_time, airfare, mileage, 1 as numsegs
from flight f
where src_city = <SRC_CITY>
union all
select cast(s.flight||'-->'||cast(f.flight_num as varchar(255)) as varchar(255)) as flight, s.src_city, f.dest_city,
s.dept_time, f.arr_time, s.airfare + f.airfare as airfare,
s.mileage + f.mileage as milage, s.numsegs + 1
from segs s join
flight f
on s.src_city = f.dest_city
where s.numsegs < 5
)
select *
from segs
where dest_city = <DEST_CITY>
order by airfare desc
limit 1;
像这样的东西:
select * from
(select flight_num, airfare from flight where src_city = ? and dest_city = ?
union
select f1.flight_num || f2.flight_num, f1.airfare+f2.airfare
from flight f1, flight f2 where f1.src_city = ? and f2.dest_city = ? and f1.dest_city = f2.src_city
union
...
) s order by airfare desc
我没有测试,因为我正在为你离开,因此可能存在需要测试的微妙问题。 这显然是家庭作业,因为没有航空公司以这种方式计划。 所以我不介意给你额外的工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.