从节点的链接表返回树结构流

时间:2014-12-09 18:45:18

标签: sql sql-server tsql

我需要输出包含节点之间链接的表的树流

Input Table:
LocationType,Source, Location

'B','A1','B1'
'C','B1','C1'
'D','C1','D1'
'E','D1','E1'
'C','B2','C2'
'D','B3','D3'
'E','D4','E4'
'B','A5','B5'
'B','A5','B6'
'C','B6','C6'
'D','C6','D7'
'D','C7','D7'
'E','D7','E8'
'D',null,'D9'
'E','D9,'E9'

可以做出的假设:

  

' A'是树的最早根,但root可以从任何节点开始

     

树向一个方向流动,即。 A< B< C< D< ë

     

没有向后链接,例如B可以链接到D但D不能链接到A,B或   ç

     

起始位置并不总是相同(并非总是A)。结尾   位置并不总是相同的(可以比E早结束)

     

可以存在多个孩子,例如A5至B5,A5至B6

     

节点可以有多个来源,例如C6至D7,C7至D7。来源可以   是空的。位置永远不能为空

由于存在大量数据,因此首选SQL性能。

期望输出:(顺序不重要)

Columns: A,B,C,D,E

'A1','B1','C1','D1','E1'
null,'B2','C2',null,null
null,'B3',null,'D3',null
null,null,null,'D4','E4'
'A5','B5',null,null,null
'A5','B6','C6','D7','E8'
null,null,'C7','D7','E8'
null,null,null,'D9','E9'

测试数据生成

CREATE TABLE Links
(
  [LocationType] CHAR(1) ,
  [Source] VARCHAR(10) ,
  [Location] VARCHAR(10)
)

INSERT INTO Links VALUES ('B','A1','B1')
INSERT INTO Links VALUES ('C','B1','C1')
INSERT INTO Links VALUES ('D','C1','D1')
INSERT INTO Links VALUES ('E','D1','E1')
INSERT INTO Links VALUES ('C','B2','C2')
INSERT INTO Links VALUES ('D','B3','D3')
INSERT INTO Links VALUES ('E','D4','E4')
INSERT INTO Links VALUES ('B','A5','B5')
INSERT INTO Links VALUES ('B','A5','B6')
INSERT INTO Links VALUES ('C','B6','C6')
INSERT INTO Links VALUES ('D','C6','D7')
INSERT INTO Links VALUES ('D','C7','D7')
INSERT INTO Links VALUES ('E','D7','E8')
INSERT INTO Links VALUES ('D',null,'D9')
INSERT INTO Links VALUES ('E','D9','E9')

1 个答案:

答案 0 :(得分:1)

听起来你需要使用递归CTE(公用表表达式)。

此查询使用您提供的数据并与您提供的示例输出相匹配:

;WITH [cte_root_items] AS
    ( -- find the root items and use ROW_NUMBER() to create an identifier for each root item
        SELECT
            ROW_NUMBER() OVER (ORDER BY CAST(RIGHT([Source], LEN([Source]) - 1) AS INT), [Source]) [SourceIdentifier],
            [Source],
            [Location]
        FROM [dbo].[Links]
        WHERE   [Source] NOT IN (SELECT [Location] FROM [dbo].[Links] WHERE [Source] IS NOT NULL)
    ),
[cte_recursion] AS
    ( -- recursively iterate through tree until all child items for each root item are found
        SELECT
            [SourceIdentifier],
            [Source],
            [Location]
        FROM [cte_root_items]

        UNION ALL

        SELECT
            A.[SourceIdentifier],
            A.[Source],
            B.[Location]
        FROM [cte_recursion] A
        INNER JOIN [dbo].[Links] B
        ON      A.[Location] = B.[Source]
    ),
[cte_root_items_combined_with_recursion] AS
    ( -- append the root items to the items found during recursion
        SELECT
            [SourceIdentifier],
            [Source] [Location]
        FROM [cte_root_items]

        UNION ALL

        SELECT
            [SourceIdentifier],
            [Location]
        FROM [cte_recursion]
    )
SELECT -- pivot the data into columns A-E
    MAX(CASE WHEN [Location] LIKE 'A%' THEN [Location] ELSE NULL END) [A],
    MAX(CASE WHEN [Location] LIKE 'B%' THEN [Location] ELSE NULL END) [B],
    MAX(CASE WHEN [Location] LIKE 'C%' THEN [Location] ELSE NULL END) [C],
    MAX(CASE WHEN [Location] LIKE 'D%' THEN [Location] ELSE NULL END) [D],
    MAX(CASE WHEN [Location] LIKE 'E%' THEN [Location] ELSE NULL END) [E]
FROM [cte_root_items_combined_with_recursion]
GROUP BY 
    [SourceIdentifier]
ORDER BY -- ordering data just to match example output, although noted as not important
    [SourceIdentifier];