【问题标题】:How does recursive CTE without JOIN work?没有 JOIN 的递归 CTE 如何工作?
【发布时间】: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

【问题讨论】:

标签: sql sql-server sql-server-2016


【解决方案1】:

首先,anchor 不需要只返回一行,它可以返回任意大小的结果集。请注意,SQL 适用于集合,而不是概念上的记录。

其次,递归的要求是从之前的运行回到CTE,但不需要加入外部表。这就是你所做的。然后此递归将继续,直到没有更多行满足第二个 WHERE 条件。

因此,您对其工作原理的解释是正确的。

【讨论】:

    猜你喜欢
    • 2018-12-13
    • 2020-01-07
    • 2013-05-24
    • 2021-10-20
    • 1970-01-01
    • 2015-10-23
    • 2015-10-29
    • 2019-03-10
    • 1970-01-01
    相关资源
    最近更新 更多