【问题标题】:What leads to this strange SQL behavior?是什么导致了这种奇怪的 SQL 行为?
【发布时间】:2011-09-30 17:09:36
【问题描述】:

运行 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

尝试如下执行上面的proc,你会得到如下结果:

EXEC dbo.Test

我是空的!

现在,改变 proc,使 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

如果你现在执行它,你会得到......

我是空的!

我是空的!

我是空的!

...ad infinitum 直到输出中断并出现此错误:

消息 217,级别 16,状态 1,程序 测试,第 16 行 最大存储量 过程、函数、触发器或视图 超出嵌套级别(限制 32)。

暂时忽略这根本不是标准做法,并且很可能有人会偶然做这样的事情,有人可以提供一些关于 SQL 2005 在这个过程的第二个化身被执行了吗?

【问题讨论】:

  • 要了解递归,首先必须了解递归。
  • 没什么神秘的。它将处理直到下一个 GO 或批次结束的其他指示符之前的所有内容作为 proc 的一部分。作为过程的一部分,最外面的 BEGIN 和 END 不是必需的语法。
  • 注意文档msdn.microsoft.com/en-us/library/ms189762(v=sql.90).aspx中BEGIN和END周围的方括号
  • @Joe,这就是答案。你应该把那篇文章写成“完整”的帖子,而不仅仅是评论。
  • 只是另一条评论,将 INT 与 '' 进行比较对我来说根本不直观。你为什么不直接说 IF @Value IS NULL?使用 IsNull 进行比较有什么意义?

标签: sql sql-server sql-server-2005 stored-procedures recursion


【解决方案1】:

您的代码按预期运行。该过程自称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】:

    正如其他人所提到的,它被称为递归。

    您可以像@Adrian 所展示的那样避免它(使用“GO”来防止 sp 调用自身),或者您也可以使用控制结构来逃避它...

    如果您想了解递归,可以学习以下示例/实验:http://msdn.microsoft.com/en-us/library/aa175801.aspx

    【讨论】:

      【解决方案3】:

      它允许 32 个嵌套调用。并且每次 Exec 调用你都会永远嵌套它。所以递归思考。

      执行过程 选择 执行 选择 执行 无限。

      一旦到达第 32 个嵌套调用,它就会达到最大值并说哇,我无法继续。

      【讨论】:

        【解决方案4】:

        递归是因为一切都被视为 proc 的一部分,而不仅仅是 BEGIN 到 END 块。

        来自我的评论:

        没有什么大谜团。它将处理直到下一个 GO 或批次结束的其他指示符之前的所有内容作为 proc 的一部分。作为过程的一部分,最外面的 BEGIN 和 END 不是必需的语法。

        【讨论】:

          【解决方案5】:

          我对这个问题的解读不是“为什么我的 SP 表现出递归?”但是“为什么递归限制为 32,我该如何解决?”

          我完全忘记了 SQL 递归就是这样死的。

          我刚刚得出的答案是使用 TRY-CATCH 和 @@NestLevel。下面是一个小型演示装置。在您的代码中,最好有一个独立的结束条件,例如用完要处理的块。

          我的代码已被编辑器破坏,我没有时间解决您的问题。

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

          去 创建过程 dbo.Nester @NestLevel INT = 0 OUT 作为 开始 声明@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
          

          结束 走 执行 dbo.Nester;

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-02-02
            • 2012-01-05
            • 1970-01-01
            • 2014-12-27
            相关资源
            最近更新 更多