Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

рекурсивен cte с функции за класиране

РЕДАКТИРАНЕ

Когато прочетете документацията на CTE относно рекурсията, ще забележите, че тя има някои ограничения, като например невъзможност за използване на подзаявки, групиране по, най-горе. Всички те включват множество редове. От ограничено тестване и проверка на плана за изпълнение, както и тестване на тази заявка

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

Мога само да заключа:

  1. Row_Number() работи в CTE, когато други таблици са обединени, за да се получи многоредов набор от резултати
  2. От резултатите от номерирането става ясно, че CTE се обработват в един ред през всички итерации, ред по ред вместо многоред по много ред, въпреки че изглежда, че се повтарят всички редове едновременно. Това би обяснило защо никоя от функциите, които се прилагат за многоредови операции, не е разрешена за рекурсивен CTE.

Въпреки че лесно стигнах до това заключение, някой очевидно е отнел много повече време, за да обяснете го в мъчителни подробности само Преди 17 месеца...

С други думи, това е естеството на реализацията на от SQL Server рекурсивен CTE, така че прозоречните функции няма да работят по начина, по който очаквате.

В полза на другите изходът е:
a           b           c           d
----------- ----------- ----------- -----------
1           1           1           2
1           2           1           2
2           3           1           2
2           4           1           2

Докато очаквате c да съдържа 1,2,1,2 вместо 1,1,1,1. Това със сигурност изглежда, че може да е грешка, тъй като няма документация, която да твърди, че функциите за прозорци не трябва да работят в рекурсивната част на CTE.

Забележка:row_number() връща bigint, така че можете да прехвърлите само anchor(c) като bigint.

Тъй като всяка итерация увеличава d, можете да извършите прозореца навън.

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

РЕДАКТИРАНЕ - прозрения

Докато отговарях на друг въпрос , играх още малко с рекурсивен CTE. Ако го стартирате без крайния ORDER BY, можете да видите как SQL Server се приближава към рекурсията. Интересно е, че в този случай се връща назад, след което прави пълна рекурсия в дълбочина на всеки ред.

Примерна таблица

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'

Рекурсивна заявка

;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

Резултатът показва CTE котвата, обработена в итерация едно, след което по каквато и да е причина всеки ред в набора за котва се рекурсира до завършване (първо в дълбочина), преди да се обработят други редове.

И все пак той има своите странни приложения, като този отговор показвата




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Групиране по нулеви и ненулеви стойности

  2. Entity Framework EF4.1 - съхранената процедура не може да бъде намерена в контейнера

  3. Как да махна таблица, ако съществува?

  4. Добавете данни за вход и се свържете към SQL с удостоверяване на SQL Server

  5. Експортирайте xml данни с помощта на BCP команда в SQL Server