Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

rekurencyjne cte z funkcjami rankingowymi

EDYTUJ

Kiedy przeczytasz dokumentację CTE dotyczącą rekurencji, zauważysz, że ma ona pewne ograniczenia, takie jak brak możliwości użycia podzapytań, grupowanie według, top. Wszystko to obejmuje wiele rzędów. Od ograniczonego testowania i sprawdzania planu wykonania, a także testowania tego zapytania

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

Mogę tylko stwierdzić:

  1. Row_Number() działa w CTE, gdy inne tabele są łączone w celu uzyskania wielowierszowego zestawu wyników
  2. Z wyników numeracji jasno wynika, że ​​CTE są przetwarzane w jednym wierszu we wszystkich iteracjach, wiersz po wierszu, a nie wiele wierszy po wielu wierszach, mimo że wydaje się, że wszystkie wiersze są wykonywane jednocześnie. To wyjaśniałoby, dlaczego żadna z funkcji, które mają zastosowanie do operacji wielowierszowych, nie jest dozwolona dla rekurencyjnego CTE.

Chociaż łatwo doszedłem do tego wniosku, ktoś najwyraźniej poświęcił dużo więcej czasu na wyjaśnij to w nieznośnych szczegółach tylko 17 miesięcy temu...

Innymi słowy, taka jest natura implementacji SQL Server rekurencyjne CTE, więc funkcje okienkowe nie będą działać tak, jak tego oczekujesz.

Dla dobra innych wynik jest następujący:
a           b           c           d
----------- ----------- ----------- -----------
1           1           1           2
1           2           1           2
2           3           1           2
2           4           1           2

Podczas gdy oczekujesz, że c będzie zawierać 1,2,1,2 zamiast 1,1,1,1. To z pewnością wygląda na to, że może to być błąd, ponieważ nie ma dokumentacji mówiącej, że funkcje okienkowe nie powinny działać w rekurencyjnej części CTE.

Uwaga:row_number() zwraca bigint, więc możesz rzutować tylko kotwicę(c) jako bigint.

Ponieważ każda iteracja zwiększa d, możesz wykonać okienkowanie na zewnątrz.

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

EDYTUJ — wgląd

Odpowiadając na kolejne pytanie , grałem trochę więcej z rekurencyjnym CTE. Jeśli uruchomisz go bez końcowego ORDER BY, możesz zobaczyć, jak SQL Server zbliża się do rekursji. Interesujące jest to, że w tym przypadku cofa się, a następnie wykonuje pełną rekurencję do pierwszej głębokości w każdym wierszu.

Przykładowa tabela

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'

Zapytanie rekurencyjne

;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

Wynik pokazuje kotwicę CTE przetworzoną w pierwszej iteracji, a następnie z dowolnego powodu każdy wiersz w zestawie zakotwiczeń jest powtarzany do końca (najpierw na głębokość) przed przetworzeniem innych wierszy.

Jednak ma swoje dziwne zastosowania, jak ta odpowiedź programy




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak znaleźć wszystkie zadania agenta SQL, które wywołują dany przechowywany proces?

  2. Tworzenie gniazda wewnątrz wyzwalacza SQL-CLR lub procedury składowanej

  3. Zastąp część łańcucha odpowiednikiem pisanym wielkimi literami z tabeli temp cross ref - tsql

  4. Uzyskaj procentową liczbę rekordów w jednym zapytaniu

  5. Entity Framework 6 — zapytania dotyczące czasu