REDIGER
Når du læser CTE-dokumentationen vedrørende rekursion, vil du bemærke, at den har nogle begrænsninger, såsom ikke at kunne bruge underforespørgsler, grupper efter, top. Disse involverer alle flere rækker. Fra begrænset test, og kontrol af udførelsesplanen, samt test af denne forespørgsel
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
Jeg kan kun konkludere:
- Række_Number() virker i en CTE, når andre tabeller sammenføjes for at producere et resultatsæt med flere rækker
- Fra resultaterne af nummereringen er det klart, at CTE'er behandles i en enkelt række gennem alle iterationer, række for række i stedet for flere rækker for flere, selvom det ser ud til at gentage alle rækker samtidigt. Dette ville forklare, hvorfor nogen af de funktioner, der gælder for operationer med flere rækker, ikke er tilladt for rekursiv CTE.
Selvom jeg let kom til denne konklusion, tog nogen åbenbart meget mere tid til at forklar det med ulidelige detaljer kun 17 måneder siden...
Med andre ord er dette arten af SQL Servers implementering af rekursiv CTE, så vinduesfunktioner vil ikke fungere, som du forventer.
Til gavn for andre er outputtet:
a b c d
----------- ----------- ----------- -----------
1 1 1 2
1 2 1 2
2 3 1 2
2 4 1 2
Mens du forventer, at c indeholder 1,2,1,2 i stedet for 1,1,1,1. Dette ser bestemt ud til, at det kunne være en fejl, da der ikke er dokumentation for, at vinduesfunktioner ikke bør fungere i den rekursive del af en CTE.
Bemærk:row_number() returnerer bigint, så du kan kun kaste anker(c) som bigint.
Da hver iteration øger d, kan du udføre vinduesvisningen udenfor.
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
EDIT - indsigt
Mens du besvarer et andet spørgsmål , Jeg spillede noget mere med rekursiv CTE. Hvis du kører det uden den endelige ORDER BY, kan du se, hvordan SQL Server nærmer sig rekursionen. Det er interessant, at den går baglæns i dette tilfælde, og derefter laver en fuld dybde-første rekursion på hver række.
Eksempeltabel
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 forespørgsel
;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
Outputtet viser CTE-ankeret behandlet i iteration 1 og derefter uanset grund hver række i ankersættet gentages til færdiggørelse (dybde-først), før andre rækker behandles.
Alligevel har det sine mærkelige anvendelser, som dette svar viser