我有下表(ObjectStates),并希望得到根和root的第一个孩子:
ID Title ParentID
1 Draft null
2 Green null
3 Red null
4 Foo 1
5 Bar 4
6 Some1 1
7 Some2 6
8 XYZ 2
9 Some3 7
我想要以下输出:
GetState(5)
-- returns root: 1, first-child: 4
GetState(6)
-- returns root: 1, first-child: 6
GetState(7)
-- returns root: 1, first-child: 6
GetState(9)
-- returns root: 1, first-child: 6
GetState(8)
-- returns root: 2, first-child: 8
所以无论我在查询的层次结构有多深 - 我总是想要根元素以及第一个子元素。如果你会考虑这棵树,我总是想要红色和蓝色的元素,无论我在树上有多深。
我可以像这样得到“根”状态:
WITH CTEHierarchy
AS (
SELECT
ID
,0 AS LEVEL
,ID AS root
FROM ObjectStates
WHERE ParentID IS NULL
UNION ALL
SELECT
ObjectStates.ID
,LEVEL + 1 AS LEVEL
,[root]
FROM ObjectStates
INNER JOIN CTEHierarchy uh ON uh.id = ObjectStates.ParentID
)
SELECT [root]
FROM CTEHierarchy
WHERE ID = @ObjectStateID
这给了我理想的根结果:
GetState(5)
-- returns root: 1
GetState(9)
-- returns root: 1
GetState(2)
-- returns root: 2
我怎样从那里穿过?那么从root中获取树中的下一个孩子?或者相反 - 获得根和第一级。递归正在打破我的脑袋。
答案 0 :(得分:2)
我正在使用此查询来遍历今天的主键外键关系,以跟踪整个路径,这个问题看起来很相似。所以只需粘贴相同的代码。您可以直接运行此代码并检查这是否是您所需要的。
此查询在CTE中再添加两列,即Hops和Path,其中Hops是元素级别,Path是从开始到结束遍历的节点。
WITH cte
AS
(
SELECT
fk.create_date
, fk.modify_date
, fkc.constraint_object_id AS ConstraintId
, OBJECT_NAME(fkc.constraint_object_id) AS ConstraintName
, OBJECT_NAME(fkc.referenced_object_id) AS PrimaryKeyTableName
, rc.name AS PrimaryKeyColumnName
, OBJECT_NAME(fk.parent_object_id) AS ForeignKeyTableName
, lc.name AS ForeignKeyColumnName
FROM sys.foreign_key_columns fkc
INNER JOIN sys.columns rc
ON rc.OBJECT_ID = fkc.referenced_object_id
AND fkc.referenced_column_id = rc.column_id
INNER JOIN sys.foreign_keys fk
ON fk.OBJECT_ID = fkc.constraint_object_id
INNER JOIN sys.columns lc
ON lc.OBJECT_ID = fk.parent_object_id
AND fkc.parent_column_id = lc.column_id
)
, cte2(create_date, modify_date, ConstraintName
, PrimaryKeyTableName, PrimaryKeyColumnName
, ForeignKeyTableName, ForeignKeyColumnName
, Hops, path ) AS
(
SELECT
create_date, modify_date, ConstraintName
, PrimaryKeyTableName, PrimaryKeyColumnName
, ForeignKeyTableName, ForeignKeyColumnName
, 1 , CAST(QUOTENAME(PrimaryKeyTableName + '.' + PrimaryKeyColumnName) AS VARCHAR(4000))
FROM cte
UNION ALL
SELECT
cte.create_date, cte.modify_date, cte.ConstraintName
, cte.PrimaryKeyTableName, cte.PrimaryKeyColumnName
, cte.ForeignKeyTableName, cte.ForeignKeyColumnName
, cte2.Hops +1, CAST(cte2.path + '-> ' +QUOTENAME(cte.PrimaryKeyTableName+ '.' + cte.PrimaryKeyColumnName) AS VARCHAR(4000))
FROM cte2 INNER JOIN cte ON cte2.ForeignKeyTableName = cte.PrimaryKeyTableName
AND cte2.ForeignKeyColumnName != cte.PrimaryKeyColumnName
)
SELECT
ConstraintName
, PrimaryKeyTableName, PrimaryKeyColumnName
, ForeignKeyTableName, ForeignKeyColumnName
, Hops, path + '-> ' + QUOTENAME(ForeignKeyTableName + '.' + ForeignKeyColumnName) AS Path
FROM cte2
只有在数据库中物理存在外键约束时,此查询才有效。
答案 1 :(得分:1)
我认为你需要首先走上层次结构,然后进入前两个层次:
WITH cteHierarchy As
(
SELECT
ID,
ParentID,
0 As Level
FROM
ObjectStates
WHERE
ID = @ObjectStateID
UNION ALL
SELECT
OS.ID,
OS.ParentID,
H.Level + 1
FROM
cteHierarchy As H
INNER JOIN ObjectStates As OS
ON H.ParentID = OS.ID
),
cteReveresedHierarchy As
(
SELECT
ID,
ROW_NUMBER() OVER (ORDER BY Level DESC) As RowNumber
FROM
cteHierarchy
)
SELECT
ID
FROM
cteReveresedHierarchy
WHERE
RowNumber In (1, 2)
;
修改强>
要将这两个项目放在一行中:
如果您可以保证永远不会从根开始,则可以将过滤器更改为WHERE RowNumber = 2
,并添加ParentID
列。但是,如果您从根开始,那么您只有一行,因此该查询将无效。
要允许查询从根开始,您需要第2行(如果存在)或第1行:
WITH cteHierarchy As
(
SELECT
ID,
ParentID,
0 As Level
FROM
ObjectStates
WHERE
ID = @ObjectStateID
UNION ALL
SELECT
OS.ID,
OS.ParentID,
H.Level + 1
FROM
cteHierarchy As H
INNER JOIN ObjectStates As OS
ON H.ParentID = OS.ID
),
cteReveresedHierarchy As
(
SELECT
ID,
ParentID,
ROW_NUMBER() OVER (ORDER BY Level DESC) As RowNumber
FROM
cteHierarchy
)
SELECT TOP 1
ParentID As [root]
ID As [FirstChild]
FROM
cteReveresedHierarchy
WHERE
RowNumber In (1, 2)
ORDER BY
RowNumber DESC
;