[英]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.