sql >> Database teknologi >  >> RDS >> Sqlserver

rekursiv cte med rangeringsfunktioner

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:

  1. Række_Number() virker i en CTE, når andre tabeller sammenføjes for at producere et resultatsæt med flere rækker
  2. 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




  1. Send tabel som parameter til sql server UDF

  2. Mysql:Opdateringsfelt for seneste rekord

  3. Skriv spark dataframe til postgres database

  4. At få ActiveRecord / Rails til at bruge faktiske mysql TIMESTAMP-kolonner