Reputation: 414
I am trying to write a SQL query in SQL Server 2012 to convert a table that has 2 columns FROM
and TO
into a table that has a numerical sequence of the described route taken if you logically follow the from-to direction. I have been struggling for hours on this and any hints would be greatly appreciated. Thanks
EXAMPLE:
Upvotes: 1
Views: 47
Reputation: 966
I assumed that you have random data on [from] or [to] column - (may be there more data in [from] column not in [to] column and vise-versa, so
if there is no problem with making an order using [from] and [to] column use the below query
with dataTest as
(
select [From], [to] , ROW_NUMBER() over(order by [From], [to]) lvl from InputTable e
)
,dataD as
(
select [From] node,(lvl*2)-1 lvl from dataTest
union all
select [To] node,(lvl*2) lvl from dataTest
)
,lastD as
(
select node, lvl, ROW_NUMBER() over(partition by node order by lvl) rn from dataD
)
select node, ROW_NUMBER() over(order by lvl) squence from lastD where rn=1 order by lvl
if your data is typically as you set in the sample you can use
with dataTest as
(
select [From], [to] ,1 lvl from InputTable e
where not exists (select 1 from InputTable e2 where e2.[to] = e.[from])
union all
select e.[from], e.[to], dataTest.lvl + 1
from dataTest join
InputTable e on dataTest.[to] = e.[from]
)
,dataD as
(
select [From] node,(lvl*2)-1 lvl from dataTest
union all
select [To] node,(lvl*2) lvl from dataTest
)
,lastD as
(
select node, lvl, ROW_NUMBER() over(partition by node order by lvl) rn
from dataD
)
select node, ROW_NUMBER() over(order by lvl) squence from lastD where rn=1 order by lvl
Hope these help you or give you an idea.
Upvotes: 0
Reputation: 1594
To answer this I'll have to assume either a sort order or a "base record". I.e. I need to know which record should be the starting point. In this example I have simply hardcoded the value of the base record.
WITH RecursiveCTE AS(
SELECT
[FROM], [TO],
1 AS SequenceNo
FROM InputTable
WHERE [FROM] = 'B' --Hardcoded value to select a base record
UNION ALL
SELECT
t.[FROM], t.[TO],
SequenceNo + 1 AS SequenceNo
FROM RecursiveCTE e
INNER JOIN InputTable t ON e.[TO] = t.[FROM]
)
--Get all records except the last one via the Recursive CTE
SELECT
[FROM] AS [Node],
SequenceNo
FROM RecursiveCTE
UNION ALL
--Get the last record in a separate query
SELECT TOP 1
[TO] AS [Node],
SequenceNo + 1 AS SequenceNo
FROM RecursiveCTE
WHERE SequenceNo = (SELECT MAX(SequenceNo) FROM RecursiveCTE)
The query uses a recursive CTE to get all records except the last one. The last record is added by the UNION ALL statement at the end.
Upvotes: 2
Reputation: 1270371
Assuming no cycles, you can use a recursive cte:
with cte as (
select e.from, e.to, 1 as lev
from example e
where not exists (select 1 from example e2 where e2.to = e.from)
union all
select e.from, e.to, cte.lev + 1
from cte join
example e
on cte.to = e.from
)
select e.from, lev
from cte;
Upvotes: 0