Sql 递归查询
我有一个包含员工记录的表格,该表格显示了员工之间的关系以及他们向谁报告:Sql 递归查询,sql,sql-server,recursion,common-table-expression,Sql,Sql Server,Recursion,Common Table Expression,我有一个包含员工记录的表格,该表格显示了员工之间的关系以及他们向谁报告: From_ID position TO_ID position ---------------------------------------- 1 Lowest_employee 3 employee 3 employee 4 employee 4 employee 5 B
From_ID position TO_ID position
----------------------------------------
1 Lowest_employee 3 employee
3 employee 4 employee
4 employee 5 BOSS
2 Lowest_employee 6 employee
6 employee 3 employee
10 Lowest_employee 50 BOSS2
我想用员工/老板ID显示如下结果:
EmployeeID BossID
--------------------
1 5
2 5
10 50
这意味着员工1和2向ID 5报告,员工10向另一个ID为50的老板报告
我知道我需要使用CTE和递归查询,但无法理解如何实现,我对CTE递归查询比较新
我读了这篇文章,但对我来说毫无意义
实现这一点所需的任何查询帮助都将非常有用。这包括设置测试数据,但我认为这正是您想要的:
WITH q (employee, boss) AS
(
SELECT fromId, toId
FROM mytable
WHERE fromId NOT IN
(
SELECT toId
FROM mytable
)
UNION ALL
SELECT employee, toId
FROM q
JOIN mytable t
ON t.fromId = boss
)
SELECT *
FROM q
WHERE boss NOT IN
(
SELECT fromId
FROM mytable
)
测试数据:
DECLARE @Table TABLE
(
From_ID int,
TO_ID int
)
INSERT INTO @Table VALUES(1,3)
INSERT INTO @Table VALUES(3,4)
INSERT INTO @Table VALUES(4,5)
INSERT INTO @Table VALUES(2,6)
INSERT INTO @Table VALUES(6,3)
INSERT INTO @Table VALUES(10,50)
查询以获取答案:
;WITH Hierarchy (Employee, Superior, QueryLevel)
AS
(
--root is all employees that have no subordinates
SELECT E.From_ID, E.TO_ID, 1
FROM @Table E
LEFT
JOIN @Table S
ON S.TO_ID = E.From_ID
WHERE S.TO_ID IS NULL
--recurse up tree to final superior
UNION ALL
SELECT H.Employee, S.TO_ID, H.QueryLevel + 1
FROM Hierarchy H
JOIN @Table S
ON S.From_ID = H.Superior
)
SELECT Employee, Superior
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Employee ORDER BY QueryLevel DESC) AS RowNumber
FROM Hierarchy
) H
WHERE RowNumber = 1
基本上,这是通过以下方式实现的:
1) 获取没有报告对象的所有员工(根)
2) 通过凸台向上循环,记录“级别”
3) 使用over/partition仅选择“final”boss这包括设置测试数据,但我认为这是您想要的: 测试数据:
DECLARE @Table TABLE
(
From_ID int,
TO_ID int
)
INSERT INTO @Table VALUES(1,3)
INSERT INTO @Table VALUES(3,4)
INSERT INTO @Table VALUES(4,5)
INSERT INTO @Table VALUES(2,6)
INSERT INTO @Table VALUES(6,3)
INSERT INTO @Table VALUES(10,50)
查询以获取答案:
;WITH Hierarchy (Employee, Superior, QueryLevel)
AS
(
--root is all employees that have no subordinates
SELECT E.From_ID, E.TO_ID, 1
FROM @Table E
LEFT
JOIN @Table S
ON S.TO_ID = E.From_ID
WHERE S.TO_ID IS NULL
--recurse up tree to final superior
UNION ALL
SELECT H.Employee, S.TO_ID, H.QueryLevel + 1
FROM Hierarchy H
JOIN @Table S
ON S.From_ID = H.Superior
)
SELECT Employee, Superior
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Employee ORDER BY QueryLevel DESC) AS RowNumber
FROM Hierarchy
) H
WHERE RowNumber = 1
基本上,这是通过以下方式实现的:
1) 获取没有报告对象的所有员工(根)
2) 通过凸台向上循环,记录“级别”
3) 使用over/partition仅选择“final”boss您可以尝试这样的方法吗
DECLARE @Employees TABLE (
EmployeeId INT,
PositionName VARCHAR(50),
ReportsToId INT);
INSERT INTO @Employees VALUES (1, 'Driver', 3);
INSERT INTO @Employees VALUES (3, 'Head of Driving Pool', 4);
INSERT INTO @Employees VALUES (4, 'Corporate Flunky', 5);
INSERT INTO @Employees VALUES (2, 'Window Cleaner', 6);
INSERT INTO @Employees VALUES (6, 'Head of Office Services', 3);
INSERT INTO @Employees VALUES (10, 'Minion', 50);
INSERT INTO @Employees VALUES (5, 'BOSS', NULL);
INSERT INTO @Employees VALUES (50, 'BOSS2', NULL);
WITH Employees AS (
SELECT
EmployeeId,
1 AS [Level],
EmployeeID AS [Path],
ISNULL(ReportsToId, EmployeeId) AS ReportsToId
FROM
@Employees
WHERE
ReportsToId IS NULL
UNION ALL
SELECT
e.EmployeeID,
x.[Level] + 1 AS [Level],
x.[Path] + e.EmployeeID AS [Path],
x.ReportsToId
FROM
@Employees e
INNER JOIN Employees x ON x.EmployeeID = e.ReportsToId)
SELECT
ec.EmployeeId,
e.PositionName,
ec.[Level],
CASE WHEN ec.ReportsToId = ec.EmployeeId THEN NULL ELSE ec.ReportsToId END AS ReportsToId --Can't really report to yourself
FROM
Employees ec
INNER JOIN @Employees e ON e.EmployeeId = ec.EmployeeId
ORDER BY
ec.[Path];
你可以试试这样的吗
DECLARE @Employees TABLE (
EmployeeId INT,
PositionName VARCHAR(50),
ReportsToId INT);
INSERT INTO @Employees VALUES (1, 'Driver', 3);
INSERT INTO @Employees VALUES (3, 'Head of Driving Pool', 4);
INSERT INTO @Employees VALUES (4, 'Corporate Flunky', 5);
INSERT INTO @Employees VALUES (2, 'Window Cleaner', 6);
INSERT INTO @Employees VALUES (6, 'Head of Office Services', 3);
INSERT INTO @Employees VALUES (10, 'Minion', 50);
INSERT INTO @Employees VALUES (5, 'BOSS', NULL);
INSERT INTO @Employees VALUES (50, 'BOSS2', NULL);
WITH Employees AS (
SELECT
EmployeeId,
1 AS [Level],
EmployeeID AS [Path],
ISNULL(ReportsToId, EmployeeId) AS ReportsToId
FROM
@Employees
WHERE
ReportsToId IS NULL
UNION ALL
SELECT
e.EmployeeID,
x.[Level] + 1 AS [Level],
x.[Path] + e.EmployeeID AS [Path],
x.ReportsToId
FROM
@Employees e
INNER JOIN Employees x ON x.EmployeeID = e.ReportsToId)
SELECT
ec.EmployeeId,
e.PositionName,
ec.[Level],
CASE WHEN ec.ReportsToId = ec.EmployeeId THEN NULL ELSE ec.ReportsToId END AS ReportsToId --Can't really report to yourself
FROM
Employees ec
INNER JOIN @Employees e ON e.EmployeeId = ec.EmployeeId
ORDER BY
ec.[Path];
我正在努力理解你在问什么,重新阅读你的问题(忘记你知道的),看看你认为对一个不了解你的问题的人来说是否有意义。事实上,他的问题很清楚你到目前为止都做了些什么?@hoangnnm真的吗?我已经读了好几遍了,我只是想知道到底发生了什么。如果你必须阅读多次才能理解,那么它就不那么清楚了。。在我看来,SQL Server文档在递归CTE部分中几乎有这样一个确切的用例:我很难理解您在问什么,请重新阅读您的问题(忘记您所知道的)看看你认为对一个对你的问题一无所知的人来说这是否有意义。事实上,他的问题很清楚你到目前为止都做了些什么?@hoangnnm真的吗?我已经读了好几遍了,我只是想知道到底发生了什么。如果你必须阅读多次才能理解,那么它就不那么清楚了。。在我看来,SQL Server文档在递归CTE部分中有一个几乎完全相同的用例:在您发布时准备了一个类似的答案,因此如果您愿意,您可以在代码中添加此项:在您发布时准备了一个类似的答案,因此,如果您愿意,您可以在代码中添加此项:这意味着我必须创建此表或其此处仅用于测试目的?仅用于测试目的-将@table替换为您已有的表:)查询的唯一问题是:)
语句终止。在语句完成之前,最大递归100已用完。
@AndreyIvanov:这很可能意味着您的数据中有循环(当然,除非您在一家管理层超过100层的公司工作)@AndreyIvanov:appendOPTION(MAXRECURSION 0)
。请注意,如果您确实有循环(您很可能有循环),则此查询将永远不会自行结束,并最终耗尽服务器资源,严重影响其性能。因此,这意味着我必须创建此表或其here仅用于测试目的?仅用于测试目的-将@table替换为您已有的表:)查询的唯一问题是:)语句终止。在语句完成之前,最大递归100已用完。
@AndreyIvanov:这很可能意味着您的数据中有循环(当然,除非您在一家管理层超过100层的公司工作)@AndreyIvanov:appendOPTION(MAXRECURSION 0)
。请注意,如果您确实有循环(您很可能有循环),此查询将永远不会自行结束,并最终耗尽服务器资源,严重影响其性能。