查询实现SQL Server中的递归查询:以MSSQL为例

1. 理解递归查询

递归查询是指在一个表中,根据表中某一行数据的关系,查找其所属行和其他相关行,以此类推,不断递归进行查询,直到满足查询条件为止。

在 SQL Server 中,可以通过使用 WITH 语句中的递归子句和递归调用来实现递归查询。WITH 语句中的递归部分包含了两个查询:基本查询和递归查询。递归查询会从基本查询中返回结果,并将这些结果与原始查询进行合并。

2. 实现递归查询

2.1 基本查询

基本查询是递归查询的起点。它必须是一个非递归查询,用于确定递归查询的起始点和终止点。

以查找 AdventureWorks 数据库中的 Employee 表中自己的直接上级为例。如下所示:

CREATE TABLE Employee (

EmployeeID int PRIMARY KEY,

Name varchar(50),

ManagerID int

)

INSERT INTO Employee (EmployeeID, Name, ManagerID)

VALUES

(1, 'Alice', NULL),

(2, 'Bob', 1),

(3, 'Charlie', 2),

(4, 'David', 1),

(5, 'Eve', 3),

(6, 'Fred', 2),

(7, 'George', 4),

(8, 'Harry', 5),

(9, 'Ivan', 6),

(10, 'Jerry', 7),

(11, 'Kevin', 9),

(12, 'Lucy', 11)

可以通过以下查询获取 Alice 的直接下属:

SELECT EmployeeID, Name, ManagerID

FROM Employee

WHERE ManagerID = 1

这里的 WHERE 子句用于确定起始点为 EmployeeID = 1 的行。

2.2 递归查询

递归查询是基于基本查询的结果,不断地重复自己。它包含两个部分:

递归终止条件:用于确定搜索应该停止的条件。

递归部分:用于在满足终止条件之前重复自己。

以查找 Alice 的所有下属为例:

WITH Subordinates AS (

SELECT EmployeeID, Name, ManagerID, 0 AS Level

FROM Employee

WHERE EmployeeID = 1 -- 基本查询

UNION ALL

SELECT e.EmployeeID, e.Name, e.ManagerID, s.Level + 1

FROM Employee e

JOIN Subordinates s ON e.ManagerID = s.EmployeeID -- 递归部分

)

SELECT EmployeeID, Name, ManagerID

FROM Subordinates

WHERE Level > 0

ORDER BY Level, EmployeeID

在这个查询中,WITH 子句中的 Subordinates 被定义为一个递归 CTE(Common Table Expression)。这个 CTE 包含两个部分:基本查询和递归部分。

在基本查询中,WHERE 子句用于确定起始点为 EmployeeID = 1 的行。由于这个查询并不需要递归,因此 Level 被设置为 0,表示这是第一级。

在递归部分中,JOIN 子句用于将 Employee 表中 ManagerID = Subordinates.EmployeeID 的行连接到 Subordinates 中。这会形成一条从雇员到领导的路径。每个递归级别的 Subordinates 都会扩展到下一级,Level 会在递归调用中增加 1。

递归终止条件是 WHERE Level > 0,它保证查询仅返回 Alice 的直接下属。如果不加限制地执行递归查询(即不加 WHERE 子句),则将返回整张 Employee 表的所有行。

3. 理解递归查询中的循环

在使用递归 CTE 时,必须确保不存在无限循环的情况。否则,查询将永远不会终止。

例如,如果要查找 Employees 表中每个员工的祖先,可以使用以下递归 CTE。在这个例子中,递归关系不是简单的父子关系,而是 Managers 表格的自引用关系:

WITH Managers AS (

SELECT EmployeeID, Name, NULL AS ManagerID

FROM Employees

UNION ALL

SELECT e.EmployeeID, e.Name, m.EmployeeID

FROM Employees e

JOIN Managers m ON e.ManagerID = m.ManagerID

)

SELECT e.Name, m.Name AS Manager, m.Manager AS GrandManager

FROM Employees e

JOIN Managers m ON e.EmployeeID = m.EmployeeID

在这个查询中,Managers CTE 包含两个部分。第一个部分是基本查询,它选择了所有没有经理的员工。第二个部分是递归查询,它选择了所有经理和他们的下属,通过 Join Managers 来创造 Managers 表格的自引用关系。这里并没有明显的 WHERE 子句来确定终止条件,而是通过递归的自引用关系来终止。

如果在这个查询中使用循环引用,例如将 EmployeeID 为 1 的员工直接或间接地设置为他自己的经理,则查询将永远不会终止,因为递归关系是无限的。

4. 总结

递归查询是一种强大的工具,可以在 SQL Server 中轻松查询复杂关系数据。使用 WITH 语句中的递归子句和递归调用,可以轻松实现递归查询,并通过递归终止条件和递归部分来控制查询结果。

在使用递归 CTE 时,必须非常小心,确保不存在无限循环的情况。如果存在循环引用,则必须手动设置递归终止条件,以确保查询能够正确终止。

数据库标签