递归 CTE 将所有祖先列为列
Recursive CTE listing all ancestors as columns
我发现了许多显示递归 CTE 的示例,这些示例将路径列为串联字符串和关卡深度。例如:
CREATE TABLE #Test
(ID varchar(20),
ParentID varchar(20)
)
INSERT INTO #Test values ('100000','HEAD');
INSERT INTO #Test values ('100100','100000');
INSERT INTO #Test values ('100200','100100');
INSERT INTO #Test values ('100300','100200');
INSERT INTO #Test values ('100400','100300');
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT * FROM CTE
DROP table #Test
有没有办法 return 每行中的每个祖先都在单独的列中?因此,而不是当前结果:
ID ParentId Path Level
100000 HEAD 100000 0
100100 100000 100000>100100 1
100200 100100 100000>100100>100200 2
100300 100200 100000>100100>100200>100300 3
100400 100300 100000>100100>100200>100300>100400 4
它将是:
Id Parent L0 L1 L2 L3 L4
100000 HEAD 100000 Null Null Null Null
100100 100000 100000 100100 Null Null Null
100200 100100 100000 100100 100200 Null Null
100300 100200 100000 100100 100200 100300 Null
100400 100300 100000 100100 100200 100300 100400
假设我知道深度的最大级别,在此示例中,最大级别为 5。SQL 服务器版本。 12.0,数据库兼容性为 120。
如果您有已知或最大级别数,请考虑 JSON。
如果不是 2016+ ...有一个类似的 XML 方法。
例子
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT A.ID
,A.ParentID
,B.*
,A.Level
FROM CTE A
Cross Apply (
Select L0 = trim(JSON_VALUE(S,'$[0]'))
,L1 = trim(JSON_VALUE(S,'$[1]'))
,L2 = trim(JSON_VALUE(S,'$[2]'))
,L3 = trim(JSON_VALUE(S,'$[3]'))
,L4 = trim(JSON_VALUE(S,'$[4]'))
,L5 = trim(JSON_VALUE(S,'$[5]'))
From ( values ( '["'+replace(path,'>','","')+'"]' ) ) A(S)
) B
Returns
更新:XML 方法
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT A.ID
,A.ParentID
,B.*
,A.Level
FROM CTE A
Cross Apply (
Select L0 = xDim.value('/x[1]','varchar(50)')
,L1 = xDim.value('/x[2]','varchar(50)')
,L2 = xDim.value('/x[3]','varchar(50)')
,L3 = xDim.value('/x[4]','varchar(50)')
,L4 = xDim.value('/x[5]','varchar(50)')
,L5 = xDim.value('/x[6]','varchar(50)')
From (Select Cast('<x>' + replace(Path,'>','</x><x>')+'</x>' as xml) as xDim) as A
) B
正如大家提到的,你需要知道你拥有的关卡数量,但这里是 PIVOT
:
的实现
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
select
*
from (
Select Id , ParentID,
'L'+ CAST(Row_Number() Over (Partition By ID Order By ID) AS VARCHAR)AS Col,
Split.value
, level
From CTE
Cross apply string_split(path,'>') as Split
) AS tbl
Pivot (Max(value) For Col IN ([L1],[L2],[L3],[L4],[L5])) AS Pvt
我发现了许多显示递归 CTE 的示例,这些示例将路径列为串联字符串和关卡深度。例如:
CREATE TABLE #Test
(ID varchar(20),
ParentID varchar(20)
)
INSERT INTO #Test values ('100000','HEAD');
INSERT INTO #Test values ('100100','100000');
INSERT INTO #Test values ('100200','100100');
INSERT INTO #Test values ('100300','100200');
INSERT INTO #Test values ('100400','100300');
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT * FROM CTE
DROP table #Test
有没有办法 return 每行中的每个祖先都在单独的列中?因此,而不是当前结果:
ID ParentId Path Level
100000 HEAD 100000 0
100100 100000 100000>100100 1
100200 100100 100000>100100>100200 2
100300 100200 100000>100100>100200>100300 3
100400 100300 100000>100100>100200>100300>100400 4
它将是:
Id Parent L0 L1 L2 L3 L4
100000 HEAD 100000 Null Null Null Null
100100 100000 100000 100100 Null Null Null
100200 100100 100000 100100 100200 Null Null
100300 100200 100000 100100 100200 100300 Null
100400 100300 100000 100100 100200 100300 100400
假设我知道深度的最大级别,在此示例中,最大级别为 5。SQL 服务器版本。 12.0,数据库兼容性为 120。
如果您有已知或最大级别数,请考虑 JSON。
如果不是 2016+ ...有一个类似的 XML 方法。
例子
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT A.ID
,A.ParentID
,B.*
,A.Level
FROM CTE A
Cross Apply (
Select L0 = trim(JSON_VALUE(S,'$[0]'))
,L1 = trim(JSON_VALUE(S,'$[1]'))
,L2 = trim(JSON_VALUE(S,'$[2]'))
,L3 = trim(JSON_VALUE(S,'$[3]'))
,L4 = trim(JSON_VALUE(S,'$[4]'))
,L5 = trim(JSON_VALUE(S,'$[5]'))
From ( values ( '["'+replace(path,'>','","')+'"]' ) ) A(S)
) B
Returns
更新:XML 方法
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT A.ID
,A.ParentID
,B.*
,A.Level
FROM CTE A
Cross Apply (
Select L0 = xDim.value('/x[1]','varchar(50)')
,L1 = xDim.value('/x[2]','varchar(50)')
,L2 = xDim.value('/x[3]','varchar(50)')
,L3 = xDim.value('/x[4]','varchar(50)')
,L4 = xDim.value('/x[5]','varchar(50)')
,L5 = xDim.value('/x[6]','varchar(50)')
From (Select Cast('<x>' + replace(Path,'>','</x><x>')+'</x>' as xml) as xDim) as A
) B
正如大家提到的,你需要知道你拥有的关卡数量,但这里是 PIVOT
:
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
select
*
from (
Select Id , ParentID,
'L'+ CAST(Row_Number() Over (Partition By ID Order By ID) AS VARCHAR)AS Col,
Split.value
, level
From CTE
Cross apply string_split(path,'>') as Split
) AS tbl
Pivot (Max(value) For Col IN ([L1],[L2],[L3],[L4],[L5])) AS Pvt