根据层次结构生成列

问题描述 投票:0回答:1

我有一个包含两列的输入表,如下所示。 Parent ID 和 Child ID 列将具有分层结构。

##输入表

家长ID 孩子ID
PR001 CR001
PR001 CR002
PR001 CR003
CR001 DR001
CR001 DR002
CR002 DR003
DR001 ER001
DR002 ER002
...... ......
XXXXX YYYYY

我应用了递归CTE方法来生成中间输出,以包括基于层次结构的级别列

##中间输出表:

家长ID 孩子ID 级别
PR001 CR001 1
PR001 CR002 1
PR001 CR003 1
CR001 DR001 2
CR001 DR002 2
CR002 DR003 3
DR001 ER001 3
DR002 ER002 3
...... ...... ...
XXXXX YYYYY N

我试图通过根据中间输出的层次结构旋转值来生成最终输出。级别以其值作为前缀的列数将基于级别列。

##期望的结果:

身份证 1级 2级 3级 级别....N
PR001 CR001 DR001 ER001
PR001 CR001 DR002 ER002
PR001 CR002 DR003
PR001 CR003
XXXX XXXX YYYY

尝试按如下方式应用枢轴,但没有帮助

SELECT ParentID, ChildID 
FROM 
(SELECT ParentID, ChildID, Level from table_1>) 
AS src 
PIVOT 
( 
count (ParentID) 
FOR [Level] 
IN ( '1' as 'Level_1', '2' as 'Level_2', '3' as 'Level_3' ]) 
) AS pvt ; 

SQL Server 中是否有任何函数可以应用于这种情况?

sql sql-server hierarchy
1个回答
0
投票

数据

IF(OBJECT_ID('tempdb..#tmp_Intermediate') is not null)
    DROP TABLE #tmp_Intermediate

SELECT
    *
INTO #tmp_Intermediate
FROM(
VALUES
    ('PR001', 'CR001', 1),
    ('PR001', 'CR002', 1),
    ('PR001', 'CR003', 1),
    ('CR001', 'DR001', 2),
    ('CR001', 'DR002', 2),
    ('CR002', 'DR003', 3), --LEVEL 3 or 2??
    ('DR001', 'ER001', 3),
    ('DR002', 'ER002', 3),
    ('ER002', 'DEMO4', 4) --added for demo
) x (ParentID, ChildID, Level)

手动查询

SELECT 
    src.ParentID,
    src.ChildID [LEVEL1],
    l2.ChildID [LEVEL2],
    l3.ChildID [LEVEL3]
FROM #tmp_Intermediate src
LEFT JOIN #tmp_Intermediate l2 on l2.ParentID = src.ChildID
LEFT JOIN #tmp_Intermediate l3 on l3.ParentID = l2.ChildID
WHERE src.level = 1

结果

家长ID 1级 2级 3级
PR001 CR001 DR001 ER001
PR001 CR001 DR002 ER002
PR001 CR002 DR003
PR001 CR003

注意: 这是使用左连接,因此您可以获得额外的列。不确定您是否需要更多解释。另外,因为它是手动的,所以 demo4 将不会显示,因为需要手动将级别 4 添加到查询中。

动态如果你想要更动态的东西,你可以这样做。

DECLARE @selectTemplate nvarchar(max) = char(13)+char(10)+char(9)+'l{{iteration}}.ChildID AS [LEVEL{{iteration}}],';
DECLARE @joinTemplate nvarchar(max) = char(13)+char(10)+'LEFT JOIN #tmp_Intermediate l{{iteration}} on l{{iteration}}.ParentID = l{{parent_iteration}}.ChildID';
DECLARE @sqlTemplate nvarchar(max) = N'SELECT 
    l1.ParentID,
    l1.ChildID,{{selects}}
FROM #tmp_Intermediate l1{{leftjoins}}
WHERE l1.ParentID not in(SELECT ChildID FROM #tmp_Intermediate)';

DECLARE @leftJoins nvarchar(max) = '';
DECLARE @selects nvarchar(max) = '';
DECLARE @interation int = 2;
DECLARE @maxLevel int = (SELECT MAX([Level]) FROM #tmp_Intermediate);

WHILE (@interation <= @maxLevel)
BEGIN   
    SET @selects += REPLACE(@selectTemplate, '{{iteration}}', CAST(@interation AS NVARCHAR))
    SET @leftJoins += REPLACE(REPLACE(
                        @joinTemplate, 
                        '{{iteration}}', CAST(@interation AS NVARCHAR)), 
                        '{{parent_iteration}}', CAST(@interation - 1 AS NVARCHAR))

    IF(@interation = @maxLevel) -- get rid of the last comma
        SET @selects = LEFT(@selects, LEN(@selects)-1)
    SET @interation += 1;
END

SET @sqlTemplate = REPLACE(REPLACE(
                    @sqlTemplate,
                    '{{selects}}', @selects),
                    '{{leftjoins}}', @leftJoins)

EXEC(@sqlTemplate);

生成的查询

SELECT 
    l1.ParentID,
    l1.ChildID [Level1],
    l2.ChildID AS [LEVEL2],
    l3.ChildID AS [LEVEL3],
    l4.ChildID AS [LEVEL4]
FROM #tmp_Intermediate l1
LEFT JOIN #tmp_Intermediate l2 on l2.ParentID = l1.ChildID
LEFT JOIN #tmp_Intermediate l3 on l3.ParentID = l2.ChildID
LEFT JOIN #tmp_Intermediate l4 on l4.ParentID = l3.ChildID
WHERE l1.level = 1

结果

家长ID 儿童ID 2级 3级 4级
PR001 CR001 DR001 ER001
PR001 CR001 DR002 ER002 演示4
PR001 CR002 DR003
PR001 CR003

这会获取当前中间表中的最高级别。然后循环构建查询的选择和连接部分。我已经使用替换来更新所需的部分,尽管您可以将其全部内联。此外,如果查询设置不同,STRING_AGG 或 STUFF 也可以工作。

如果你做这样的事情,我会将代码放在存储过程中以使其更加有序。

© www.soinside.com 2019 - 2024. All rights reserved.