РЕДАКТИРОВАТЬ
Когда вы прочитаете документацию 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
я могу только сделать вывод:
- Row_Number () работает в CTE, когда другиетаблицы объединяются для получения многострочного результирующего набора
- Из результатов нумерации ясно, что 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, так что вы можете использовать только якорь (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
EDIT - понимание
Отвечая на другой вопрос link , я немного поиграл с рекурсивным 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, обработанную в первой итерации, затем по любой причине каждая строка в наборе привязок возвращается к завершению (сначала в глубину) перед обработкой других строк.
Все же у него есть свои странные применения, как этот ответ показывает