如何在递归cte中使用排名函数? 这是一个简单的例子,展示了我的尝试:
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, c, d) as ( select a, b, cast(0 as int), 1 from cte union all select a, b, cast(ROW_NUMBER() over (partition by a order by b) as int), d+1 from rcte where d < 2 ) select * from rcte where d=2 order by a, b
为什么没有排名?告诉我我的错误
答案 0 :(得分:11)
当您阅读有关递归的CTE文档时,您会注意到它有一些限制,例如无法使用子查询,分组,顶部。这些都涉及多行。从有限的测试,检查执行计划,以及测试此查询
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
我只能得出结论:
虽然我很容易得出这个结论,但显然有人花了很多时间才 explain it in excruciating detail 仅 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,因此您可以将锚点(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
<小时/>
在回答another question 链接时,我使用递归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
输出显示在迭代1中处理的CTE锚点,然后无论出于何种原因在处理其他行之前,锚点集中的每一行都被递归到完成(深度优先)。
然而它有其奇怪的用途,因为this answer显示