我有一个包含两列的输入表,如下所示。 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 中是否有任何函数可以应用于这种情况?
数据
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 也可以工作。
如果你做这样的事情,我会将代码放在存储过程中以使其更加有序。