【发布时间】:2021-05-22 16:45:35
【问题描述】:
我了解传统的 CTE 和递归,如下例所示:
-- Create an Employee table.
CREATE TABLE dbo.MyEmployees
(
EmployeeID SMALLINT NOT NULL,
FirstName NVARCHAR(30) NOT NULL,
LastName NVARCHAR(40) NOT NULL,
Title NVARCHAR(50) NOT NULL,
DeptID SMALLINT NOT NULL,
ManagerID INT NULL,
CONSTRAINT PK_EmployeeID PRIMARY KEY CLUSTERED (EmployeeID ASC)
);
-- Populate the table with values.
INSERT INTO dbo.MyEmployees VALUES
(1, N'Ken', N'Sánchez', N'Chief Executive Officer',16,NULL)
,(273, N'Brian', N'Welcker', N'Vice President of Sales',3,1)
,(274, N'Stephen', N'Jiang', N'North American Sales Manager',3,273)
,(275, N'Michael', N'Blythe', N'Sales Representative',3,274)
,(276, N'Linda', N'Mitchell', N'Sales Representative',3,274)
,(285, N'Syed', N'Abbas', N'Pacific Sales Manager',3,273)
,(286, N'Lynn', N'Tsoflias', N'Sales Representative',3,285)
,(16, N'David',N'Bradley', N'Marketing Manager', 4, 273)
,(23, N'Mary', N'Gibson', N'Marketing Specialist', 4, 16);
WITH DirectReports(ManagerID, EmployeeID, Title, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, Title, 0 AS EmployeeLevel
FROM dbo.MyEmployees
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, e.Title, EmployeeLevel + 1
FROM dbo.MyEmployees AS e
INNER JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID
)
SELECT
ManagerID, EmployeeID, Title, EmployeeLevel
FROM
DirectReports
ORDER BY
ManagerID;
锚点是 1 行(返回 R0),然后发生递归,让员工在该行是经理(返回 R1)。然后获取 R1 中记录为经理的员工(返回 R2)等等。最后将所有这些结果联合起来。
我已经制定了一个 CTE,其中锚点返回多于 1 行,并且递归查询中不涉及连接条件。例如,下面的查询返回开始日期和结束日期之间所有日期的记录 - 在下面的查询中有 2 个锚点(Bob 和 Phil),并且 UNION ALL 查询不执行任何连接。这样的 CTE 是如何工作的?例如 - 第一个它将获取锚行。那么递归查询将为两个锚行返回 date+1 吗?那么下一个递归查询将为所有先前的行返回 date+1+1 吗?依此类推,然后最后用做一个 UNION ALL。我找不到此类 SQL 的在线示例,但它对我来说效果很好,因为它为开始日期和结束日期之间的每个日期返回行。我无法想象没有 JOIN 的递归 CTE 的实际工作原理,感谢您的帮助。
DROP TABLE IF EXISTS tempdb.dbo.#dates
CREATE TABLE #dates
(
NAME VARCHAR(50),
START DATETIME,
[END] DATETIME
)
INSERT INTO #dates
VALUES
('Bob','2014-10-30','2014-11-02'),
('Phil','2014-10-10','2014-10-15')
;WITH cte AS
(
SELECT
NAME,
[START],
[END]
FROM
#dates
UNION ALL
SELECT
NAME,
DATEADD(DAY, 1, start),
[end]
FROM
cte
WHERE
start < [END]
)
SELECT *
FROM cte
ORDER BY 1,2
【问题讨论】:
-
有或没有连接,逻辑没有什么不同。 rCTE 不断递归,直到没有行返回。在这种情况下,当
start大于或等于end。 -
不过,如果你想为两个日期之间的每一天生成一个日期,你最好用一个计数;它们的性能要高得多。还有一些额外的注释。不要在
ORDER BY中使用序数位置。 Bad Habits to Kick : ORDER BY ordinal。同样;is a statement terminator,它位于每个语句的末尾,而不是需要正确终止previous语句的语句的开头。 -
统计表是指包含所有日期的单列表吗?
标签: sql sql-server sql-server-2016