Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

rekursiver cte mit Ranking-Funktionen

BEARBEITEN

Wenn Sie die CTE-Dokumentation zur Rekursion lesen, werden Sie feststellen, dass es einige Einschränkungen gibt, wie z. Diese beinhalten alle mehrere Zeilen. Vom begrenzten Testen und Überprüfen des Ausführungsplans bis hin zum Testen dieser Abfrage

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

Ich kann nur schließen:

  1. Row_Number() funktioniert in einem CTE, wenn andere Tabellen verknüpft werden, um eine mehrzeilige Ergebnismenge zu erzeugen
  2. Aus den Ergebnissen der Nummerierung geht klar hervor, dass CTEs in einer einzigen Zeile durch alle Iterationen verarbeitet werden, Zeile für Zeile statt Multizeile für Multirow, obwohl es so aussieht, als würden alle Zeilen gleichzeitig iteriert. Dies würde erklären, warum alle Funktionen, die für mehrzeilige Operationen gelten, für rekursive CTE nicht zulässig sind.

Obwohl ich leicht zu diesem Schluss kam, hat sich offensichtlich jemand viel mehr Zeit genommen, um erklären Sie es in unerträglichen Details nur Vor 17 Monaten...

Mit anderen Worten, dies ist die Natur der SQL Server-Implementierung von rekursiver CTE, sodass Windowing-Funktionen nicht so funktionieren, wie Sie es erwarten.

Für andere ist die Ausgabe:
a           b           c           d
----------- ----------- ----------- -----------
1           1           1           2
1           2           1           2
2           3           1           2
2           4           1           2

Während Sie erwarten, dass c 1,2,1,2 anstelle von 1,1,1,1 enthält. Dies scheint sicherlich ein Fehler zu sein, da es keine Dokumentation gibt, die besagt, dass Fensterfunktionen im rekursiven Teil eines CTE nicht funktionieren sollten.

Hinweis:row_number() gibt bigint zurück, sodass Sie nur den Anker (c) als bigint werfen können.

Da jede Iteration d erhöht, könnten Sie die Fensterung außerhalb durchführen.

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

BEARBEITEN - Einblick

Bei der Beantwortung von eine weitere Frage , ich habe noch etwas mit rekursivem CTE gespielt. Wenn Sie es ohne das abschließende ORDER BY ausführen, können Sie sehen, wie sich SQL Server der Rekursion nähert. Es ist interessant, dass es in diesem Fall rückwärts geht und dann eine vollständige Tiefenrekursion für jede Zeile durchführt.

Beispieltabelle

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'

Eine rekursive Abfrage

;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

Die Ausgabe zeigt den CTE-Anker, der in Iteration eins verarbeitet wurde, dann aus welchem ​​Grund auch immer Jede Zeile im Ankersatz wird bis zur Vervollständigung rekursiv ausgeführt (Tiefe zuerst), bevor andere Zeilen verarbeitet werden.

Dennoch hat es seine seltsamen Verwendungen, wie diese Antwort zeigt