sql >> Databáze >  >> RDS >> Sqlserver

rekurzivní cte s hodnotícími funkcemi

UPRAVIT

Když si přečtete dokumentaci CTE týkající se rekurze, všimnete si, že má určitá omezení, jako například nemožnost používat poddotazy, group-by, top. To vše zahrnuje několik řádků. Od omezeného testování a kontroly prováděcího plánu, stejně jako testování tohoto dotazu

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

Mohu jen uzavřít:

  1. Row_Number() funguje v CTE, když jsou ostatní tabulky spojeny a vytvářejí víceřádkovou sadu výsledků
  2. Z výsledků číslování je zřejmé, že CTE jsou zpracovány v jednom řádku ve všech iteracích, řádek po řádku namísto více řádek po více řádcích, i když se zdá, že iterují všechny řádky současně. To by vysvětlovalo, proč žádná z funkcí, které se vztahují na víceřádkové operace, není povolena pro rekurzivní CTE.

Ačkoli jsem k tomuto závěru došel snadno, někomu evidentně trvalo mnohem více času, než vysvětlete to do mučivých podrobností pouze před 17 měsíci...

Jinými slovy, toto je povaha implementace SQL Serveru rekurzivní CTE, takže funkce okna nebudou fungovat tak, jak očekáváte.

Ve prospěch ostatních je výstupem:
a           b           c           d
----------- ----------- ----------- -----------
1           1           1           2
1           2           1           2
2           3           1           2
2           4           1           2

Zatímco očekáváte, že c bude obsahovat 1,2,1,2 místo 1,1,1,1. Určitě se zdá, že by to mohla být chyba, protože neexistuje žádná dokumentace, která by tvrdila, že funkce oken by neměly fungovat v rekurzivní části CTE.

Poznámka:row_number() vrací bigint, takže jako bigint můžete použít pouze kotvu(c).

Vzhledem k tomu, že každá iterace zvyšuje d, můžete provádět okno mimo.

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

UPRAVIT – přehled

Při odpovídání na další dotaz , hrál jsem ještě něco s rekurzivním CTE. Pokud jej spustíte bez konečného ORDER BY, můžete vidět, jak se SQL Server blíží k rekurzi. Je zajímavé, že se v tomto případě vrátí zpět a poté provede úplnou rekurzi do hloubky v každém řádku.

Ukázková tabulka

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'

Rekurzivní dotaz

;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

Výstup zobrazuje kotvu CTE zpracovanou v iteraci jedna, poté z jakéhokoli důvodu každý řádek v sadě ukotvení je před zpracováním dalších řádků přepracován až do konce (do hloubky).

Přesto má své zvláštní využití, jako tato odpověď pořady




  1. Analýza a porovnání dat pomocí Coldfusion a MySQL

  2. Jak průběžně odstraňovat cokoli staršího než 10 nových záznamů z databáze MySQL (možná v JPQL/JPA)

  3. Chování jedinečného indexu, sloupce varchar a (prázdných) mezer

  4. Automatické mazání zaseknutých procesů v MS SQL Server