编辑
当你阅读 CTE 关于递归的文档时,你会注意到它有一些限制,比如不能使用子查询、group-by、top。这些都涉及多行。从有限的测试,检查执行计划,以及测试这个查询
with cte as (
select 1 a, 1 b union all select 1, 2 union all select 1, 3 union all select 2, 4
)
, rcte (a, b, c, d) as (
select a, b, cast(0 as int), 1
from cte
union all
select r.a, cte.b, cast(ROW_NUMBER() over (order by r.b) as int), r.d+1
from rcte r inner join cte on cte.a=r.a
where r.d < 2
)
select *
from rcte
where d=2
order by a, b
我只能得出结论:
- 当连接其他表以生成多行结果集时,Row_Number() 确实在 CTE 中工作
- 从编号的结果来看,很明显,CTE 在所有迭代中在单行中处理,逐行而不是逐行多行,即使它似乎同时迭代所有行。这可以解释为什么适用于多行操作的任何函数都不允许用于递归 CTE。
虽然我很容易就得出了这个结论,但显然在 17 个月前就有人花了更多的时间详细解释它 ……
换句话说,这是SQL Server 实现递归 CTE 的本质,因此窗口函数不会按您期望的方式工作。
为了他人的利益,输出是:
a b c d
----------- ----------- ----------- -----------
1 1 1 2
1 2 1 2
2 3 1 2
2 4 1 2
而您期望 c 包含 1,2,1,2 而不是 1,1,1,1。 这当然看起来可能是一个错误,因为没有文档说窗口函数不应该在 CTE 的递归部分中工作。
注意:row_number()返回 bigint,因此您可以只将 anchor(c) 转换为 bigint。
由于每次迭代都会增加 d,因此您可以在外部执行窗口化。
with cte as (
select 1 a, 1 b union all select 1, 2 union all select 2, 3 union all select 2, 4
)
, rcte (a, b, d) as (
select a, b, 1
from cte
union all
select a, b, d+1
from rcte
where d < 2
)
select a,b, ROW_NUMBER() over (partition by a,d order by b) c,d
from rcte
--where d=2
order by d, a, b
编辑 - 洞察力
在回答另一个问题链接时,我用递归 CTE 玩了一些。如果您在没有最终 ORDER BY 的情况下运行它,您可以看到 SQL Server 是如何接近递归的。有趣的是,在这种情况下它会倒退,然后对每一行进行完整的深度优先递归。
样品表
create table Testdata(SomeID int, OtherID int, Data varchar(max))
insert Testdata select 1, 9, '18,20,22,alpha,beta,gamma,delta'
insert Testdata select 2, 6, ''
insert Testdata select 3, 8, '11,12,.'
insert Testdata select 4, 7, '13,19,20,66,12,232,1232,12312,1312,abc,def'
insert Testdata select 5, 8, '17,19'
递归查询
;with tmp(SomeID, OtherID, DataItem, Data) as (
select SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from Testdata
union all
select SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from tmp
where Data > ''
)
select SomeID, OtherID, DataItem, Data
from tmp
-- order by SomeID
输出显示在第一次迭代中处理的 CTE 锚,然后无论出于何种原因,锚集中的每一行都在处理其他行之前递归完成(深度优先)。
然而,正如这个答案所示,它确实有其奇怪的用途