Что приводит к этому странному поведению SQL? - PullRequest
1 голос
/ 07 июля 2011

Запуск SQL 2005 X64.

Сначала создайте следующий сохраненный процесс в базе данных:

CREATE PROCEDURE dbo.Test 
    @Value int = null

AS

BEGIN

    IF (IsNull(@Value, '') = '')
        SELECT '*I am NULL!*'
    ELSE
        SELECT 'I am ' + CONVERT(varchar(20), @Value)

END

Попробуйте выполнить вышеупомянутый процесс следующим образом, и вы получите следующий результат:

EXEC dbo.Test

Я NULL!

Теперь измените процедуру так, чтобы инструкция EXEC была частью самого sproc:

ALTER PROCEDURE dbo.Test 
    @Value int = null

AS

BEGIN

    IF (IsNull(@Value, '') = '')
        SELECT 'I am NULL!'
    ELSE
        SELECT 'I am ' + CONVERT(varchar(20), @Value)

END

EXEC dbo.Test

Если вы выполните ее сейчас, вы получите...

Я NULL!

Я NULL!

Я NULL!

... до бесконечности до тех пор, пока выход не прервется с этой ошибкой:

Сообщение 217, уровень 16, состояние 1, проверка процедуры, строка 16, максимумпревышен уровень вложенности хранимой процедуры, функции, триггера или представления (предел 32).

На данный момент игнорируем, что это совсем не стандартная практика и что, скорее всего, кто-то сделает что-то подобноеМожет ли кто-то случайно сообщить о том, что «думает» SQL 2005, когда исполняется второе воплощение этого процесса?

Ответы [ 5 ]

6 голосов
/ 08 июля 2011

Рекурсия заключается в том, что все считается частью процесса, а не только блоком НАЧАЛО КОНЕЦ.

Из моего комментария:

Никакой большой загадкиОн будет обрабатывать все до следующего GO или другого индикатора конца партии как часть процесса.Внешние BEGIN и END не требуют синтаксиса как части процедуры.

6 голосов
/ 07 июля 2011

Ваш код ведет себя как ожидалось. Процедура вызывает себя рекурсивно .

Если вы не хотите этого, попробуйте следующее:

ALTER PROCEDURE dbo.Test 
    @Value int = null

AS

BEGIN

    IF (IsNull(@Value, '') = '')
        SELECT 'I am NULL!'
    ELSE
        SELECT 'I am ' + CONVERT(varchar(20), @Value)

END

GO

EXEC dbo.Test

Если вы хотите, чтобы использовал рекурсию, вы должны определить базовый случай (AKA "условие выхода"), который заставит хранимую процедуру выйти из стека рекурсии.

1 голос
/ 07 июля 2011

Позволяет 32 вложенных звонка.и с каждым вызовом Exec вы вкладываете его навсегда.Так что думайте рекурсивно.

Exec proc Select Exec Select exec Infinately.

Как только он достигает 32-го вложенного вызова, он достигает своего максимума и говорит, что я не могу продолжить.

1 голос
/ 07 июля 2011

Это называется рекурсия, как уже упоминали другие.

Вы можете избежать этого, как показывал @Adrian (используя 'GO', чтобы запретить sp вызывать себя), или вы также можете избежать его, используя управляющую структуру ...

Вот пример / эксперимент, который вы можете изучить, если хотите узнать о рекурсии: http://msdn.microsoft.com/en-us/library/aa175801.aspx

0 голосов
/ 28 апреля 2017

Мое чтение вопроса не "Почему мой SP показывает рекурсию?"но «Почему рекурсия ограничена 32 и как мне обойти это?»

Я полностью забыл, что SQL Recursion умирает именно так.

Ответ, который я только что разработал, заключается в следующем:используйте TRY-CATCH и @@ NestLevel.Ниже приведен небольшой демонстрационный стенд.В вашем коде было бы гораздо лучше иметь независимое конечное условие, например, когда заканчиваются фрагменты для обработки.

Мой код был искажен редактором, у меня нет времени на решение ваших проблем.

BEGIN TRY DROP PROCEDURE dbo.Nester END TRY BEGIN CATCH END catch

GO СОЗДАТЬ ПРОЦЕДУРУ dbo.Nester @NestLevel INT = 0, КАК НАЧИНАЕТСЯ @MaxActNestLevel INT = 40;

SELECT @NestLevel += 1;

PRINT (CONVERT(sysname, @@NestLevel) + '    ' + CONVERT(sysname, @NestLevel))

IF @NestLevel < @MaxActNestLevel
BEGIN TRY
    EXEC dbo.Nester @NestLevel OUT
END TRY
BEGIN CATCH
    PRINT 'Catch Block'
    PRINT (ERROR_NUMBER())

    SELECT @NestLevel += 1;

    IF @@NestLevel < 30 --AND ERROR_NUMBER() = 217
    BEGIN
        EXEC dbo.Nester @NestLevel OUT
    END
    ELSE 
        THROW

END CATCH

END GO EXEC dbo.Nester;

...