簡體   English   中英

使用Postgres進行遞歸/分層查詢

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM