2016-07-27 4 views
0

Требование состоит в том, чтобы написать табличную функцию, которая принимает 2 параметра.Как работает рекурсивное общее табличное выражение?

@start_number (a integer value) 
@end_number (a integer value) 

Функция возвращает таблицу, содержащую число между @start_number и @end_number, включая как числа параметров.

Вот код:

Declare @start_number int 
Declare @end_number int 
Declare @max_recursion int 

Set @start_number = 10 
Set @end_number = 100 
Set @max_recursion = (@end_number - @start_number) 

Declare @numbers table(number int) --start 
;with numbers(number) as 
(
select @start_number as number 
union all 
select number + 1 from numbers where number between @start_number and @end_number - 1 
) 

insert into @numbers(number) 
select number from numbers option(maxrecursion 10000) 
select number from @numbers  --end 

Запрос дает желаемый output.But, мне нужно объяснение от start до end, как эти линии работают Какова цель max recursion здесь?

+0

Почему бы вам не попросить автора об этом? –

+0

Я спрашиваю автора и предлагаю другой подход, если вы его не заметили. – FDavidov

+0

@FDavidov Я думаю, что вопрос был для Zahid ... –

ответ

1
-- table variable declaration 
Declare @numbers table(number int) --start 

-- CTE declaration 
;with numbers(number) as 
(-- CTE body 
-- anchor 
select @start_number as number -- [a] 
union all 

-- recursive call to itself 
select number + 1 from numbers -- [r] 

-- recursion limit 
where number between @start_number and @end_number - 1 
) 

insert into @numbers(number) 

-- insert into @numbers table all values from CTE 
select number from numbers option(maxrecursion 10000) 

-- pretty obvious select 
select number from @numbers  --end 

Таблица переменной будет работать практически как обычная таблица. О разнице между временной таблицей и табличной переменной см. this question или msdn.

CTE как вложенный запрос, временный результирующий набор. Основное различие заключается в том, что оно может быть самонаправленным (как в вашем случае). Вы объявляете CTE только с одной колонкой number. Указание столбцов вручную не требуется, если они могут быть разрешены. Это recursive CTE - выберите номер и присоединитесь к себе, добавив +1 к номеру. Таким образом, каждая последующая строка будет иметь +1 предыдущей строке. Когда вы выбираете из CTE, выполняется анкер select @start_number as number. Чем он сцеплен со всем, вернула форму (добавив +1).

Давайте идти шаг за шагом в КТР:

on [a] return 1 
on [r] add +1 to everything from self ([a] and [r]): 
    on [a] return 1 
    on [r] add +1 to everything from self ([a] and [r]): 
     on [a] return 1 
     on [r] add +1 to everything from self ([a] and [r]): 
      on [a] return 1 
      on [r] add +1 to everything from self ([a] and [r]): 
       ... 

Таким образом, ваш набор результатов (каждый уровень гнездо внутри { и }):

{1, {1, {1, {1, ...} +1 } +1 } +1 } 

где {1} +1 => {2}, {1, {1} +1 } +1 => {1, 2} +1 => {2, 3} и так далее

Так что, если вы не будете ограничивать этот запрос, вложение будет бесконечным. Вот почему появляются recursion limit. Двигатель просто отфильтрует ваш бесконечный запрос и прекратит вложенность, так как при любом последующем выборе он получит значение, которое не будет соответствовать вам фильтру, и этот выбор вернет пустой набор результатов. Например, вы фильтровать значения менее чем 10:

.... 
on [a] return 1 -- will result 8, match 
on [r] add +1 to everything from self ([a] and [r]): 
    on [a] return 1 -- will result in 9, match 
    on [r] add +1 to everything from self ([a] and [r]): -- (STOP) 
     on [a] return 1 -- will result in 10, not match 
         -- nothing else will be queried as on (STOP) empty result returned 
         -- so, recursion stopped 

Обратите внимание, что если вы неправильно фильтровать в рекурсивном вызове, например,

where number between @start_number + 1 and @end_number - 1 

что очень первый рекурсивный вызов будет возвращаться к вам @start_number, которая не соответствует вам фильтр, и не рекурсии не будет происходить.

На самом деле, фильтр

where number between @start_number and @end_number - 1 

является чрезмерным. Это может быть упрощено до

where number < @end_number 

Примечания, в обеих случаях фильтра не будет соответствовать @end_number, как добавляет +1 к предыдущему значению.Так как вы начинаете с @start_number (якорь) и конкатенации с с помощью рекурсии, которая возвращает

{ @start_number, ..., @end_number -1 } +1 

результат будет

{ @start_number, @start_number +1, ..., @end_number -1 +1 } 
-- ^^^^^^^^^^^^^ - anchor 
--  recursion - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

Hint в выберите заявление option(maxrecursion 10000) ручной коррекции максимального по умолчанию уровня рекурсии (по умолчанию это 100 , действительными значениями являются [0..32767]). Таким образом, ваша конкретная функция ограничена до 10000 значений, и если вы будете пытаться произвести более 10000 значений, вы получите эту ошибку:

The statement terminated. The maximum recursion 10000 has been exhausted before statement completion.

Примечание, фильтр, который ограничивает рекурсию должны быть размещены внутри КТР, не там, где используйте CTE, так как в этом случае движок продолжит итерацию через CTE, ища следующее значение соответствия, но ваш CTE будет буквально бесконечным и произойдет ошибка.

Остальная часть кода довольно проста - форма результата CTE вставлена ​​в таблицу @numbers и затем просто выбрана.

Переменная @max_recursion может быть удалена, поскольку она не используется (и не может использоваться внутри статьи OPTION).

Смежные вопросы