该应用程序用于员工ID获取所有经理
declare @emp table (id int primary key, mgr int);
insert into @emp values
(1, null)
, (2, 1)
, (3, 2)
, (4, null)
, (5, 4);
select * from @emp;
; with cte as
( select e.id, e.mgr, cnt = 1
from @emp e
union all
select e.id, e.mgr, cnt + 1
from @emp e
join cte
on cte.mgr = e.id
)
select id, mgr, cnt
from cte
where id = 3;
以上只返回id = 3的单行。我得到的是预期但不是我想要的。我想在3点开始(锚定)并获得经理链。
如果我对锚点进行硬编码,我会得到所需的结果 见下文:
; with cte as
( select e.id, e.mgr, cnt = 1
from @emp e
where e.id = 3
union all
select e.id, e.mgr, cnt + 1
from @emp e
join cte
on cte.mgr = e.id
)
select id, mgr, cnt
from cte;
我的问题是如何仅在cte上的where
分配锚点(顶部)?
如果不在where
中,是否还有另一种方法来分配锚点(不是在cte中硬编码)?
答案 0 :(得分:3)
您需要在cte中保留起始位置:
declare @emp table (id int primary key, mgr int);
insert into @emp values
(1, null)
, (2, 1)
, (3, 2)
, (4, null)
, (5, 4);
; with cte as
( select e.id ori, e.id, e.mgr, cnt = 1
from @emp e
union all
select cte.ori, e.id, e.mgr, cnt + 1
from @emp e
join cte
on cte.mgr = e.id
)
select ori, id, mgr, cnt
from cte
where cte.ori = 3;
结果:
+-----+----+------+-----+
| ori | id | mgr | cnt |
+-----+----+------+-----+
| 3 | 3 | 2 | 1 |
| 3 | 2 | 1 | 2 |
| 3 | 1 | NULL | 3 |
+-----+----+------+-----+
答案 1 :(得分:2)
我只进行了一次更改并删除了(看似)不必要的列:
declare @emp table (id int primary key, mgr int);
insert into @emp values
(1, null)
, (2, 1)
, (3, 2)
, (4, null)
, (5, 4);
select * from @emp;
; with cte as
( select e.id, e.mgr
from @emp e
union all
select cte.id, e.mgr
from @emp e
join cte
on cte.mgr = e.id
)
select id, mgr
from cte
where id = 3;
结果是:
id | mgr
3 | 2
3 | 1
3 | NULL
答案 2 :(得分:1)
您必须决定是否
WHERE mgr IS NULL
或显式ID)并向下移动链接,或者where not exists(SELECT 1 FROM @emp AS x WHERE e.id=x.mgr)
添加到锚点)并向上或向where e.id=3
)并向上移动。一般建议是:从最窄的锚点开始!
您在问题中陈述的第一个查询(以及此处的其他答案)将执行巨大过量从处开始创建每个任意链重叠结果。
由于递归CTE是一个隐藏的RBAR ,引擎无法预测其结果并将创建满载 - 只是为了抛弃大部分内容。
如果这是一个1:n
关系(总是1 mgr on-top),向上移动会快得多。从给定的子节点开始每个级别只有一步 - 就是这样。
在1}} 之后将过滤器应用于递归CTE,意味着创建任何可能的链只是为了将其中大部分放弃...
你的第二种方法是我能想到的最好方法。
所以问题是:你为什么要在最后应用这个过滤器?