BEWERKEN
Wanneer u de CTE-documentatie over recursie leest, zult u merken dat deze een aantal beperkingen heeft, zoals het niet kunnen gebruiken van subquery's, group-by, top. Deze omvatten allemaal meerdere rijen. Van beperkt testen en het controleren van het uitvoeringsplan, tot het testen van deze query
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
Ik kan alleen maar concluderen:
- Row_Number() werkt wel in een CTE, wanneer andere tabellen worden samengevoegd om een resultaatset met meerdere rijen te produceren
- Uit de resultaten van de nummering blijkt duidelijk dat CTE's in een enkele rij door alle iteraties worden verwerkt, rij voor rij in plaats van meerdere rijen voor meerdere rijen, hoewel het lijkt alsof alle rijen tegelijkertijd worden herhaald. Dit zou verklaren waarom een van de functies die van toepassing zijn op bewerkingen met meerdere rijen niet is toegestaan voor recursieve CTE.
Hoewel ik gemakkelijk tot deze conclusie kwam, nam iemand duidelijk veel meer tijd om leg het uit in ondragelijke details alleen 17 maanden geleden...
Met andere woorden, dit is de aard van de SQL Server-implementatie van de recursieve CTE, dus vensterfuncties werken niet zoals u verwacht.
Ten behoeve van anderen is de output:
a b c d
----------- ----------- ----------- -----------
1 1 1 2
1 2 1 2
2 3 1 2
2 4 1 2
Terwijl je verwacht dat c 1,2,1,2 bevat in plaats van 1,1,1,1. Dit lijkt zeker een bug te zijn, aangezien er geen documentatie is om te zeggen dat vensterfuncties niet zouden moeten werken in het recursieve deel van een CTE.
Opmerking:row_number() geeft bigint terug, zodat je alleen het anker (c) als bigint kunt casten.
Aangezien elke iteratie d verhoogt, zou je de windowing buiten kunnen uitvoeren.
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
BEWERKEN - inzicht
Tijdens het beantwoorden van een andere vraag , speelde ik wat meer met recursieve CTE. Als u het uitvoert zonder de laatste ORDER BY, kunt u zien hoe SQL Server de recursie benadert. Het is interessant dat het in dit geval achteruit gaat en vervolgens een volledige diepte-eerste recursie doet op elke rij.
Voorbeeldtabel
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'
Een recursieve zoekopdracht
;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
De uitvoer toont het CTE-anker dat in iteratie één is verwerkt en vervolgens om welke reden dan ook elke rij in de ankerset wordt opnieuw voltooid (diepte eerst) voordat andere rijen worden verwerkt.
Toch heeft het zijn vreemde toepassingen, zoals dit antwoord shows