SQL Server查询以查找从城市A到城市B的所有路由以及传输

时间:2017-01-17 09:51:50

标签: sql sql-server algorithm

在面试开发人员职位时,我被问到这个问题。

任务:将航班路线存储在SQL Server表写入查询中,该查询将查找从城市A到城市B的所有路线,无需转移,只需一次或两次转移。例如,你有路线:

| From         | To
----------------------
Los Angeles     London   
Los Angeles     New York
New York        London
Los Angeles     Seattle
Seattle         Paris
Paris           London

您需要找到所有从洛杉矶到伦敦的接送路线。结果应该是这样的

Route
------------------------
Los Angeles->London
Los Angeles->New York->London
Los Angeles->Seattle->Paris->London

我的解决方案就是这个

select [From] + '->' + [To] as [Route] from Routes where [From] = 'Los Angeles' and [To] = 'London'
union 
select r1.[From] + '->' + r1.[To] + '->' + r2.[To] as [Route] from Routes as r1 
join Routes as r2 on r1.[To] = r2.[From]
where r1.[From] = 'Los Angeles' and r2.[To] = 'London'
union 
select r1.[From] + '->' + r1.[To] + '->' + r2.[To] + '->' + r3.[To] as [Route] from Routes as r1 
join Routes as r2 on r1.[To] = r2.[From]
join Routes as r3 on r2.[To] = r3.[From]
where r1.[From] = 'Los Angeles' and r3.[To] = 'London'

工作但看起来不是很好,如果我们需要找到包含3,4,5或更多转移的路线,我们需要添加具有更复杂选择的新联合。

对于这个特殊的例子,它很好,因为人们通常对超过2次转移的航班不感兴趣。但总的来说,我认为这个查询看起来更好。也许有人可以使用更一般的查询来解决此任务。感谢。

3 个答案:

答案 0 :(得分:15)

是的,您为所有RDB提供了通用的SQL解决方案。我看到你有一个提示 - SQL Server。它支持递归CTE,可用于解决此任务。

    let promise = $state.params.searchPromise;

    promise.then(r => {
        console.log('search result',r);
    })

因此,这里有SQL Server的通用解决方案,您可以通过传输计数来过滤它们。

答案 1 :(得分:4)

我个人......在这个例子中我会选择CTE,但这也可以用动态SQL来完成,因为你不知道有多少人愿意去做,做一个函数和动态SQL会为你点上他的点,并根据需要进行多次连接,现在可以有更多格式并添加这些箭头和东西......但似乎它的工作原理

您可以将@sql执行到临时表中并使其他条件停止London

if object_id('tempdb..#Test') is not null drop table #Test
create table #Test ([From] nvarchar(20), [To] nvarchar(20))

insert into #Test ([From], [To])
values 
('Log Angeles', 'London'),
('Log Angeles', 'New York'),
('New York', 'London'),
('Log Angeles', 'Seattle'),
('Seattle', 'Paris'),
('Paris', 'London')

declare @StopsCount int = 2
declare @beginingStop int = 2

declare @sqlHeader nvarchar(max) = 'select t' + cast(@StopsCount as nvarchar) + 
                               '.[From], t' + + cast(@StopsCount as nvarchar) + 
                               '.[To] '

declare @sqlQuery nvarchar(max) = 'from #Test t' + cast(@StopsCount as nvarchar)
while @StopsCount > 0
BEGIN

    set @StopsCount = @StopsCount - 1

    set @sqlQuery = @sqlQuery + ' left join #Test t' + cast(@StopsCount as nvarchar) +
                  ' on t' + cast(convert(int, (@StopsCount + 1)) as nvarchar) + '.[To]' +
                  ' = t' + cast(@StopsCount as nvarchar) + '.[From]'                      
    set @sqlHeader = @sqlHeader + ', t' + cast(@StopsCount as nvarchar) + '.[To]'
END

set @sqlQuery = @sqlHeader + @sqlQuery + ' where t' 
+ cast(@beginingStop as nvarchar) + '.[From] = ''Log Angeles'''

execute (@sqlQuery)

答案 2 :(得分:2)

感谢您提出这个好问题。

这个答案不适用于OP的特定RDBMS(not SQL Server),但我特别针对我熟悉的Oracle编写。

查询使用分层查询而不是递归CTE,SQL Server中LEVEL <= 3等于TransfersCount <= 2

WITH routes AS
(
    SELECT 'Los Angeles' from_place, 'London' to_place FROM DUAL UNION ALL
    SELECT 'Los Angeles', 'New York' FROM DUAL UNION ALL
    SELECT 'New York', 'London' FROM DUAL UNION ALL
    SELECT 'Los Angeles', 'Seattle' FROM DUAL UNION ALL
    SELECT 'Seattle', 'Paris' FROM DUAL UNION ALL
    SELECT 'Seattle', 'Los Angeles' FROM DUAL UNION ALL
    SELECT 'Paris', 'London' FROM DUAL 
)
SELECT SUBSTR(SYS_CONNECT_BY_PATH(from_place , '->'), 3) || '->' || to_place AS path
FROM routes
WHERE to_place = 'London'
START WITH from_place = 'Los Angeles'
CONNECT BY NOCYCLE  PRIOR to_place = from_place 
    AND to_place <> 'Los Angeles' 
    AND LEVEL <= 3 ;