2011-07-07 91 views
1

運行SQL 2005 X64。是什麼導致了這種奇怪的SQL行爲?

首先,在數據庫中創建以下存儲過程:

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 

嘗試執行上面的PROC如下,你會得到下面的結果:

EXEC dbo.Test 

我NULL!

現在,改變PROC,以便EXEC語句是存儲過程本身的一部分:

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!

... 循環往復直到輸出改變了這個錯誤:

Msg 217, Level 16, State 1, Procedure Test, Line 16 Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).

暫時忽略,這根本不是一個標準的做法,並且最有可能有人會做這樣的事情這只是偶然,有人可能會提供一些低級的洞察什麼SQL 2005是「思考」,當這個過程的第二個化身被執行?

+13

要理解遞歸,首先必須瞭解遞歸。 – Joe

+8

沒有什麼神祕的。它將處理所有內容,直到批處理結束時的下一個GO或其他指示符作爲proc的一部分。作爲過程的一部分,最外面的BEGIN和END不是必需的語法。 – Joe

+0

請注意文檔http://msdn.microsoft.com/en-us/library/ms189762(v=sql.90).aspx – Joe

回答

6

您的代碼正常工作。該程序自稱爲recursively

如果希望出現這種情況,試試這個:

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 

如果確實想使用遞歸,你必須定義一個base case這將使存儲(又名「退出條件」)過程退出遞歸堆棧。

+2

+1。遞歸是原始答案的問題 – niktrs

1

就像其他人提到的那樣,它被稱爲遞歸。

你能避免它作爲@Adrian顯示(採用「走出去」,以防止SP的自稱),或者你也可以使用控制結構逃脫它...

下面是一個示例/實驗中,你可以學習,如果你想了解遞歸:http://msdn.microsoft.com/en-us/library/aa175801.aspx

1

它允許32個嵌套調用。並且每次Exec呼叫都會永久嵌套它。所以遞歸思考。

Exec的PROC 選擇 Exec的 選擇 EXEC 無限。

一旦它達到第32個嵌套調用它達到其最大值,並說我不能繼續下去。

5

遞歸是因爲一切都被認爲是proc的一部分,而不僅僅是BEGIN到END塊。

從我的評論:

沒有太大的神祕感。它將處理所有內容,直到批處理結束時的下一個GO或其他指示符作爲proc的一部分。作爲過程的一部分,最外面的BEGIN和END不是必需的語法。

+0

+1 – Joe

0

我對這個問題的閱讀不是「爲什麼我的SP展示遞歸?」但是「爲什麼遞歸限制爲32,我如何解決這個問題?」

我完全忘記了SQL遞歸就這樣死在你身上。

我剛纔制定的答案是利用TRY-CATCH和@@ NestLevel。下面是一個小型示範裝置。在你的代碼中,有一個獨立的結束條件會好得多,例如用盡塊來處理。

我的代碼已被編輯弄壞了,我沒有時間處理你的問題。

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

GO CREATE PROCEDURE dbo.Nester @NestLevel INT = 0 OUT AS BEGIN DECLARE @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;

相關問題