REDIGERA
När du läser CTE-dokumentationen angående rekursion kommer du att märka att den har vissa begränsningar, som att inte kunna använda subqueries, group-by, top. Dessa involverar alla flera rader. Från begränsad testning och kontroll av exekveringsplanen, samt testning av denna fråga
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
Jag kan bara dra slutsatsen:
- Row_Number() fungerar i en CTE, när andra tabeller sammanfogas för att skapa en resultatuppsättning med flera rader
- Från resultaten av numreringen är det tydligt att CTE:er bearbetas på en enda rad genom alla iterationer, rad för rad istället för flera rader för flera rader, även om det verkar iterera alla rader samtidigt. Detta skulle förklara varför någon av funktionerna som gäller för flerradsoperationer inte är tillåtna för rekursiv CTE.
Även om jag lätt kom fram till denna slutsats, tog någon uppenbarligen mycket mer tid att förklara det i plågsamma detaljer endast 17 månader sedan...
Med andra ord är det här typen av SQL Servers implementering av rekursiv CTE, så fönsterfunktionerna kommer inte att fungera som du förväntar dig.
Till förmån för andra är utgången:
a b c d
----------- ----------- ----------- -----------
1 1 1 2
1 2 1 2
2 3 1 2
2 4 1 2
Medan du förväntar dig att c ska innehålla 1,2,1,2 istället för 1,1,1,1. Det här verkar verkligen som om det kan vara ett fel, eftersom det inte finns någon dokumentation som säger att fönsterfunktioner inte ska fungera i den rekursiva delen av en CTE.
Obs:row_number() returnerar bigint, så du kan kasta bara ankaret(c) som bigint.
Eftersom varje iteration ökar d, kan du utföra fönstret utanför.
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
REDIGERA - insikt
Medan du svarar på en annan fråga , Jag spelade lite mer med rekursiv CTE. Om du kör det utan den slutliga ORDER BY kan du se hur SQL Server närmar sig rekursionen. Det är intressant att den går bakåt i det här fallet och sedan gör en full djup-första rekursion på varje rad.
Exempeltabell
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'
En rekursiv fråga
;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
Utdata visar CTE-ankaret som bearbetades i iteration ett, sedan av vilken anledning som helst varje rad i ankaruppsättningen återupptas till slutförandet (djupet-först) innan andra rader bearbetas.
Ändå har det sina konstiga användningsområden, som det här svaret visar