Brute force:
declare @t table (
[from] char(3),
[to] char(3),
[time] float);
insert into @t
([from], [to], [time])
values
('PHL', 'DTW', 2.25),
('DTW', 'PVG', 15.15),
('PHL', 'ORD', 3.15),
('ORD', 'PVG', 16.20);
declare @src char(3) = 'PHL',
@dst char(3) = 'PVG';
with cteAnchor as (
select case @src
when [from] then [to]
when [to] then [from]
end as [layover], [time]
, [time] as [total]
, cast([from]+'-'+[to] as varchar(max)) as [path]
, 1 as [flights]
from @t
where @src in ([from], [to]))
, cteRecursive as (
select [layover], [time], [total], [path], [flights]
from cteAnchor
union all
select case r.layover
when [from] then [to]
when [to] then [from]
end as [layover]
, t.[time]
, t.[time] + r.[total] as [total]
, r.[path] + ' ' +t.[from]+'-'+t.[to] as [path]
, r.[flights] + 1
from @t t
join cteRecursive r
on (t.[from] = r.[layover] and 0 = charindex(t.[to], r.[path]))
or
(t.[to] = r.[layover] and 0 = charindex(t.[from], r.[path]))
)
select top(1) [flights], [total], [path] from cteRecursive
where @dst = [layover]
order by [total] asc;
Answer:
total path
17.4 PHL-DTW DTW-PVG
Edit note: I have modified the implementation of the CTE with one which is resilient to cycles and is also actually correct.