sql >> Databasteknik >  >> RDS >> Sqlserver

rekursiv cte med rankningsfunktioner

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:

  1. Row_Number() fungerar i en CTE, när andra tabeller sammanfogas för att skapa en resultatuppsättning med flera rader
  2. 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




  1. Kan `mysqlcheck` hjälpa mig att lösa databasproblem utan att skada min databas?

  2. Infogar bildfil med PyQt5 i MySQL-databastabellkolumnen

  3. beräkna summan av alla siffror i en kolumn

  4. Laravel 5:kontrollera om användaren tillhör moderatorlistan innan du tillåter honom att redigera