【问题标题】:How does a recursive CTE eliminate duplicates?递归 CTE 如何消除重复项?
【发布时间】:2015-11-04 03:50:41
【问题描述】:

我正在使用 SQL Server 2014 Express 在 AdventureWorks2012 数据库中学习递归 CTE。我想我主要得到下面的例子(取自Beginning T-SQL 3rd Edition),但我不太明白为什么递归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;

它工作正常,但是当我尝试通过临时表重新创建它来了解发生了什么时,问题就来了。我创建了一系列临时表,将一个输出插入到下一个查询的输入中,并重新创建递归 CTE 所做的事情。

--Anchor (Level 0)
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
into #orgchart
from employee
where managerid is null

然后我使用该临时表重新创建第一级递归,此时它只是递归 CTE,但带有临时表。

--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 员工的重复行(所有字段重复)。这是有道理的 - UNION ALL 之后的第二个查询将返回以前的级别以及新的递归级别,并且 UNION ALL 不会重复。如果我再做一轮递归,那么 2 级员工也会重复,以此类推。

我知道我可以将 UNION ALL 更改为 UNION 以删除重复项,但我想了解为什么递归 CTE 也不会产生重复项?它使用 UNION ALL,所以我不明白重复数据删除的来源。删除重复项是递归 CTE 的固有部分吗?

我正在尝试发布所有结果集,但如果需要它们来理解问题,请告诉我,我会发布它们。提前致谢。

【问题讨论】:

  • 你不是用SQL中的union自己造成了重复吗?如果您删除它,您应该只将正确的项目放入临时文件中。表,然后合并结果。

标签: sql-server tsql recursion duplicates sql-server-2014


【解决方案1】:

不同之处在于,当您填充#orgchart2 时,您将包含#orgchart 中的所有行。因此,现在当您创建#orgchart3(代表第三级递归)时,您将加入来自#orgchart 和#orgchart2 的行。

因此,当您在#orgchart3 中创建第三层时,它与#orgchart 和#orgchart2 中的行相关,而它应该只与#orgchart2 相关。相反,您的第三级包括比第二级高一级的行,但也比锚级别高一级,因此您正在复制行,因为您已经在第二级中拥有比锚级别高一级的行。

优化器知道不要对递归 CTE 执行此操作。每个级别的递归只查看前一个级别并忽略之前的所有级别。因此不会创建重复项。

如果您在填充 #orgchart2 和 #orgchart3 时省略了 UNION ALL 的上半部分,然后最终生成所有三个临时表的单个 UNION ALL,您将模拟优化器的作用。

【讨论】:

  • 这不是解释,我们都知道为什么 op 的脚本会产生重复,但真正的问题是 cte 如何处理这个问题。我的意思是,不仅仅是优化器知道不这样做的事情。我已经使用 cte 多年了,但这一直在我脑海中浮现。
猜你喜欢
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
  • 1970-01-01
  • 1970-01-01
  • 2018-07-07
  • 2017-08-22
相关资源
最近更新 更多