我无法获得声明以便在航班上停止所有中途停留。
我的桌子上有flightroutes,如下所示,它有一个源机场和一个目的地机场。 现在我想从机场A到机场B获得最短的飞行时间(最少的中途停留),没有从A到B的直接路线所以我必须将几条路线连接在一起。
所以例如,如果我想从18到1403我想要获得路线
(18 > 24 | 24 > 87 | 87 > 1403)
而不是
(18 > 24 | 24 > 87 | 87 > 99| 99 > 1403)
以下是一些测试数据
src_apid | dst_apid
---------+----------
18 | 24
24 | 87
87 | 99
87 | 1403
99 | 18
99 | 1403
我的尝试看起来像这样:
WITH rejkabrest (
src_apid,
dst_apid
) AS (
SELECT
src_apid,
dst_apid
FROM
routes
WHERE
src_apid = 18
UNION ALL
SELECT
a.src_apid,
a.dst_apid
FROM
routes a
INNER JOIN rejkabrest b ON a.src_apid = b.dst_apid
WHERE b.dst_apid = 1403
) SELECT
src_apid, dst_apid
FROM
rejkabrest;
但这样我才能获得所有从源头18机场开始的路线。如果我尝试另一种方式,我会遇到循环问题。
希望你们能帮助我。非常感谢提前!
答案 0 :(得分:3)
使用connect by nocycle
和功能rank()
:
select path
from (
select r.*, rank() over (order by lvl) rnk
from (select routes.*, level lvl,
sys_connect_by_path(src_apid, '->')||'->'||dst_apid path
from routes
connect by nocycle src_apid = prior dst_apid and src_apid <> 1403
start with src_apid = 18) r
where dst_apid = 1403 )
where rnk = 1
演示:
with routes (src_apid, dst_apid ) as (
select 18, 24 from dual union all
select 24, 87 from dual union all
select 87, 99 from dual union all
select 87, 1403 from dual union all
select 99, 18 from dual union all
select 99, 1403 from dual )
select path
from (
select r.*, rank() over (order by lvl) rnk
from (select routes.*, level lvl,
sys_connect_by_path(src_apid, '->')||'->'||dst_apid path
from routes
connect by nocycle src_apid = prior dst_apid and src_apid <> 1403
start with src_apid = 18) r
where dst_apid = 1403 )
where rnk = 1
PATH
--------------------
->18->24->87->1403
答案 1 :(得分:3)
这是一种递归构建路径的方法。使用CYCLE
子句可以避免循环异常。您可以使用Oracle KEEP FIRST
从找到的路径获得最短路径。
with cte (dst_apid, path, stops) as
(
select dst_apid, src_apid || ' > ' || dst_apid as path, 0 as stops
from routes
where src_apid = 18
union all
select r.dst_apid, cte.path || ' > ' || r.dst_apid, cte.stops + 1
from cte
join routes r on r.src_apid = cte.dst_apid
where cte.dst_apid <> 1403
)
cycle dst_apid set cycle to 1 default 0
select max(path) keep (dense_rank first order by stops)
from cte
where cte.dst_apid = 1403;
除KEEP FIRST
外,这是标准SQL。您可以使用SELECT path FROM cte WHERE dst_apid = 1403 FETCH FIRST 1 ROW ONLY
代替此标准。从12c开始,Oracle支持此语法。
答案 2 :(得分:1)
如果你想要每次飞行一行,想到的唯一解决方案是两个递归查询。第一个建立航班路线,编号为1,1.1,1.2,1.1.1等;秒收集属于同一路线的航班。相当复杂:
with cte1 (routepart, pos, src_apid, dst_apid) as
(
select to_char(rownum) as routepart, 1 as pos, src_apid, dst_apid
from routes
where src_apid = 18
union all
select cte1.routepart || '-' || rownum, pos + 1, r.src_apid, r.dst_apid
from cte1
join routes r on r.src_apid = cte1.dst_apid
where cte1.dst_apid <> 1403
)
cycle src_apid set cycle to 1 default 0
, cte2 (route, routepart, pos, src_apid, dst_apid) as
(
select routepart as route, routepart, pos, src_apid, dst_apid
from cte1
where dst_apid = 1403
union all
select cte2.route, cte1.routepart, cte1.pos, cte1.src_apid, cte1.dst_apid
from cte1
join cte2 on cte2.routepart like cte1.routepart || '%'
and nvl(length(regexp_replace(cte2.routepart, '[[:digit:]]', '')), 0) =
nvl(length(regexp_replace(cte1.routepart, '[[:digit:]]', '')), 0) + 1
)
cycle src_apid set cycle to 1 default 0
select pos, src_apid, dst_apid
from
(
select
cte2.*,
rank() over (order by length(regexp_replace(route, '[[:digit:]]', ''))) as rn
from cte2
)
where rn = 1
order by route, pos;
如果您不想要关系,请使用ROW_NUMBER
代替RANK
。