递归 CTE 如何消除重复项?
How does a recursive CTE eliminate duplicates?
我正在使用 SQL Server 2014 Express 在 AdventureWorks2012 数据库中学习递归 CTE。我想我得到的主要是下面的例子(取自 Beginning T-SQL 第 3 版),但我不太明白为什么递归 CTE 不产生重复项。
下面是我试图理解的递归 CTE,它是一个标准的员工 - 经理层次结构。
;with orgchart (employeeid, managerid, title, level, node) as (
--Anchor
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
from employee
where managerid is null
union all
--Recursive
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join orgchart oc on oc.employeeid = emp.managerid
)
select employeeid
, managerid
, space(level * 3) + title 'title'
, level
, node
from orgchart
order by node;
它工作正常,但是当我试图通过 temp tables 重新创建它来了解发生了什么时,问题就来了。我创建了一系列临时 tables 以将一个输出插入下一个查询的输入并重新创建递归 CTE 的功能。
--Anchor (Level 0)
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
into #orgchart
from employee
where managerid is null
然后我使用那个 temp table 重新创建第一级递归,此时它只是递归 CTE,但有 temp tables。
--Anchor + 1 level
select *
into #orgchart2
from #orgchart
union all
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join #orgchart oc on oc.employeeid = emp.managerid
到目前为止一切顺利,结果很有意义。然后我又做了一次,但这是它开始崩溃的地方:
--Anchor + 2 levels
select *
into #orgchart3
from #orgchart2
union all
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join #orgchart2 oc on oc.employeeid = emp.managerid
此输出开始为 1 级员工的 return 重复行(所有字段重复)。这是有道理的 - UNION ALL 之后的第二个查询将 return 以前的级别以及新的递归级别,并且 UNION ALL 不会重复。如果我再做一轮递归,2级员工也重复,依此类推。
我知道我可以将 UNION ALL 更改为 UNION 以删除重复项,但我想了解为什么递归 CTE 也不会生成重复项?它使用 UNION ALL 所以我不明白重复数据删除的来源。删除重复项是递归 CTE 的固有部分吗?
我正在尝试 post 所有结果集,但如果需要他们来理解问题,请告诉我,我会 post 他们。提前致谢。
不同之处在于,当您填充#orgchart2 时,您将包括#orgchart 中的所有行。因此,现在当您创建#orgchart3(代表第 3 级递归)时,您将加入来自#orgchart 和#orgchart2 的行。
所以当您在#orgchart3 中创建第三层时,它与#orgchart 和#orgchart2 中的行相关,而它应该只与#orgchart2 相关。相反,您的第三级包括比第二级高一级的行,但也包括比锚定级高一级的行,因此您正在复制行,因为第二级中已经有比锚定级高一级的行。
优化器知道不使用递归 CTE 执行此操作。每一层递归只看前一层,而忽略所有在它之前的递归。因此不会创建重复项。
如果您在填充#orgchart2 和#orgchart3 时省略了 UNION ALL 的上半部分,然后最终生成所有三个临时表的单个 UNION ALL,那么您将模拟优化器的操作。
我正在使用 SQL Server 2014 Express 在 AdventureWorks2012 数据库中学习递归 CTE。我想我得到的主要是下面的例子(取自 Beginning T-SQL 第 3 版),但我不太明白为什么递归 CTE 不产生重复项。
下面是我试图理解的递归 CTE,它是一个标准的员工 - 经理层次结构。
;with orgchart (employeeid, managerid, title, level, node) as (
--Anchor
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
from employee
where managerid is null
union all
--Recursive
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join orgchart oc on oc.employeeid = emp.managerid
)
select employeeid
, managerid
, space(level * 3) + title 'title'
, level
, node
from orgchart
order by node;
它工作正常,但是当我试图通过 temp tables 重新创建它来了解发生了什么时,问题就来了。我创建了一系列临时 tables 以将一个输出插入下一个查询的输入并重新创建递归 CTE 的功能。
--Anchor (Level 0)
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
into #orgchart
from employee
where managerid is null
然后我使用那个 temp table 重新创建第一级递归,此时它只是递归 CTE,但有 temp tables。
--Anchor + 1 level
select *
into #orgchart2
from #orgchart
union all
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join #orgchart oc on oc.employeeid = emp.managerid
到目前为止一切顺利,结果很有意义。然后我又做了一次,但这是它开始崩溃的地方:
--Anchor + 2 levels
select *
into #orgchart3
from #orgchart2
union all
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join #orgchart2 oc on oc.employeeid = emp.managerid
此输出开始为 1 级员工的 return 重复行(所有字段重复)。这是有道理的 - UNION ALL 之后的第二个查询将 return 以前的级别以及新的递归级别,并且 UNION ALL 不会重复。如果我再做一轮递归,2级员工也重复,依此类推。
我知道我可以将 UNION ALL 更改为 UNION 以删除重复项,但我想了解为什么递归 CTE 也不会生成重复项?它使用 UNION ALL 所以我不明白重复数据删除的来源。删除重复项是递归 CTE 的固有部分吗?
我正在尝试 post 所有结果集,但如果需要他们来理解问题,请告诉我,我会 post 他们。提前致谢。
不同之处在于,当您填充#orgchart2 时,您将包括#orgchart 中的所有行。因此,现在当您创建#orgchart3(代表第 3 级递归)时,您将加入来自#orgchart 和#orgchart2 的行。
所以当您在#orgchart3 中创建第三层时,它与#orgchart 和#orgchart2 中的行相关,而它应该只与#orgchart2 相关。相反,您的第三级包括比第二级高一级的行,但也包括比锚定级高一级的行,因此您正在复制行,因为第二级中已经有比锚定级高一级的行。
优化器知道不使用递归 CTE 执行此操作。每一层递归只看前一层,而忽略所有在它之前的递归。因此不会创建重复项。
如果您在填充#orgchart2 和#orgchart3 时省略了 UNION ALL 的上半部分,然后最终生成所有三个临时表的单个 UNION ALL,那么您将模拟优化器的操作。