Ron*_*erg 3 sql t-sql sql-server recursion common-table-expression
假设我有一个递归表(例如,有经理的员工)和一个0..nid 大小列表.如何找到这些ID的最低公共父级?
例如,如果我的表看起来像这样:
Id | ParentId
---|---------
1 | NULL
2 | 1
3 | 1
4 | 2
5 | 2
6 | 3
7 | 3
8 | 7
Run Code Online (Sandbox Code Playgroud)
然后以下几组ID导致以下结果(第一个是角落情况):
[] => 1 (or NULL, doesn't really matter)
[1] => 1
[2] => 2
[1,8] => 1
[4,5] => 2
[4,6] => 1
[6,7,8] => 3
Run Code Online (Sandbox Code Playgroud)
这该怎么做?
编辑:请注意,在所有情况下,父级都不是正确的术语.它是树中所有路径中的最低公共节点.最低公共节点也可以是节点本身(例如,在这种情况下[1,8] => 1,节点1不是节点的父节点1而是节点1本身).
亲切的问候,罗纳德
这是一种做法; 它使用递归CTE来查找节点的祖先,并在输入值上使用"CROSS APPLY"来获得共同的祖先; 你只需更改@ids(表变量)中的值:
----------------------------------------- SETUP
CREATE TABLE MyData (
Id int NOT NULL,
ParentId int NULL)
INSERT MyData VALUES (1,NULL)
INSERT MyData VALUES (2,1)
INSERT MyData VALUES (3,1)
INSERT MyData VALUES (4,2)
INSERT MyData VALUES (5,2)
INSERT MyData VALUES (6,3)
INSERT MyData VALUES (7,3)
INSERT MyData VALUES (8,7)
GO
CREATE FUNCTION AncestorsUdf (@Id int)
RETURNS TABLE
AS
RETURN (
WITH Ancestors (Id, ParentId)
AS (
SELECT Id, ParentId
FROM MyData
WHERE Id = @Id
UNION ALL
SELECT md.Id, md.ParentId
FROM MyData md
INNER JOIN Ancestors a
ON md.Id = a.ParentId
)
SELECT Id FROM Ancestors
);
GO
----------------------------------------- ACTUAL QUERY
DECLARE @ids TABLE (Id int NOT NULL)
DECLARE @Count int
-- your data (perhaps via a "split" udf)
INSERT @ids VALUES (6)
INSERT @ids VALUES (7)
INSERT @ids VALUES (8)
SELECT @Count = COUNT(1) FROM @ids
;
SELECT TOP 1 a.Id
FROM @ids
CROSS APPLY AncestorsUdf(Id) AS a
GROUP BY a.Id
HAVING COUNT(1) = @Count
ORDER BY a.ID DESC
Run Code Online (Sandbox Code Playgroud)
如果节点没有严格提升,则更新:
CREATE FUNCTION AncestorsUdf (@Id int)
RETURNS @result TABLE (Id int, [Level] int)
AS
BEGIN
WITH Ancestors (Id, ParentId, RelLevel)
AS (
SELECT Id, ParentId, 0
FROM MyData
WHERE Id = @Id
UNION ALL
SELECT md.Id, md.ParentId, a.RelLevel - 1
FROM MyData md
INNER JOIN Ancestors a
ON md.Id = a.ParentId
)
INSERT @result
SELECT Id, RelLevel FROM Ancestors
DECLARE @Min int
SELECT @Min = MIN([Level]) FROM @result
UPDATE @result SET [Level] = [Level] - @Min
RETURN
END
GO
Run Code Online (Sandbox Code Playgroud)
和
SELECT TOP 1 a.Id
FROM @ids
CROSS APPLY AncestorsUdf(Id) AS a
GROUP BY a.Id, a.[Level]
HAVING COUNT(1) = @Count
ORDER BY a.[Level] DESC
Run Code Online (Sandbox Code Playgroud)