SQL Server 2000:如何退出存储过程?

84

如何在存储过程的中途退出?

我有一个存储过程,在尝试调试时想要提前结束。我已经尝试使用RETURNRAISERROR语句,但是存储过程仍在继续运行:

CREATE PROCEDURE dbo.Archive_Session @SessionGUID uniqueidentifier AS

    print 'before raiserror'
    raiserror('this is a raised error', 18, 1)
    print 'before return'
    return -1
    print 'after return'

[snip]

我知道它一直在运行,因为我遇到了一个后面的错误。我没有看到任何打印输出。如果我注释掉存储过程的大部分内容:

CREATE PROCEDURE dbo.Archive_Session @SessionGUID uniqueidentifier AS

    print 'before raiserror'
    raiserror('this is a raised error', 18, 1)
    print 'before return'
    return -1
    print 'after return'

   /*
     [snip]
   */

然后我不会再遇到错误,可以看到结果:

before raiserror
Server: Msg 50000, Level 18, State 1, Procedure Archive_Session, Line 5
this is a raised error
before return

问题是:我该如何在 SQL Server 存储过程中退出?


21
欢迎来到加州旅馆…… =) - OMG Ponies
1
咕?(添加字符以使我的“咕?”查询至少包含15个字符) - Ian Boyd
13
哦,我明白了,“你永远不能离开”。<IanBoyd slow="true"/> - Ian Boyd
2
这个问题的存在表明SQL存在一些微妙的问题,但另一方面,它是一种在函数式语言侧面添加过程方法的语言,所以你期望什么呢? - quillbreaker
7个回答

107
您可以使用RETURN立即停止存储过程的执行。引用自 Books Online

无条件从查询或过程中退出。 RETURN是立即且完整的,可以在任何时候用于从过程、批处理或语句块中退出。跟随RETURN的语句不会被执行。

出于偏执,我尝试了您的示例,它确实输出了PRINT,并立即停止了执行。

1
你能想到任何原因,导致调用 return 不会无条件地退出一个过程吗? - Ian Boyd
8
如果你已经开始一个事务或者有一个打开的游标,请在退出过程时小心处理。 - No Refunds No Returns
我无法做到 - 你能否发布一个完整的存储过程示例,当你运行它时,不会立即退出RETURN?这样我们就有一个确切的示例可以审查。 - AdaTheDev
2
这个在TRY-CATCH中可行吗?如果我在TRY中调用RETURN,那么它会执行CATCH吗? - al_sweets

31

除非您指定20或更高的严重性,否则raiserror不会停止执行。请参见MSDN文档

通常的解决方法是在每个raiserror后包含一个return

if @whoops = 1
    begin
    raiserror('Whoops!', 18, 1)
    return -1
    end

1
仅由sysadmin固定服务器角色成员或具有ALTER TRACE权限的用户才能指定19到25之间的严重级别。 - Forgotten Semicolon
29
是的,在大多数情况下,raiserror 函数是以出人意料的方式发挥作用的(以及金色错别字奖杯),因此它可以被认为是最令人惊讶的函数。 - Andomar
1
+1. 对于这个问题来说并不是一个真正的答案,但是"这个答案很有用"。 - Ian Boyd

12

将其放在一个 TRY/CATCH 中。

当在 TRY 块中以 11 或更高的严重性级别运行 RAISERROR 时,它会转移控制到相关联的 CATCH 块。

参考资料:MSDN

编辑:这适用于 MSSQL 2005+,但我看到您已经澄清您正在使用 MSSQL 2000。 我会将其保留供参考。


5
+1 SQL Server 2000不能回答我的问题,但对于试图进行异常处理的人来说,它可能是一种有用的技术。 - Ian Boyd
我真的很喜欢这个。但是...唉...又要学习新的东西了。 - No Refunds No Returns

9
我发现了为什么RETURN不能无条件地从存储过程中返回。我看到的错误是在存储过程被编译时出现的,而不是在执行时出现的。
考虑一个虚构的存储过程:
CREATE PROCEDURE dbo.foo AS

INSERT INTO ExistingTable
EXECUTE LinkedServer.Database.dbo.SomeProcedure

即使这个存储过程存在错误(可能是因为对象具有不同数量的列,可能在表中有一个时间戳列,也可能存储过程不存在),你仍然可以保存它。你可以保存它,因为你正在引用一个链接服务器。
但是当你实际执行存储过程时,SQL Server会编译它,并生成一个查询计划。
我的错误不是发生在第114行,而是第114行。SQL Server无法编译存储过程,所以它失败了。
这就是为什么RETURN没有返回,因为它甚至还没有开始

1
当然,您在编写插入语句时绝不能不指定列。 - HLGEM
2
当然,我想编写不指定列的插入代码。现在必须指定它们,源表添加的其他列将会丢失。 - Ian Boyd

5

这在这里起作用。

ALTER PROCEDURE dbo.Archive_Session
    @SessionGUID int
AS 
    BEGIN
        SET NOCOUNT ON
        PRINT 'before raiserror'
        RAISERROR('this is a raised error', 18, 1)
        IF @@Error != 0 
            RETURN
        PRINT 'before return'
        RETURN -1
        PRINT 'after return'
    END
go

EXECUTE dbo.Archive_Session @SessionGUID = 1

返回

before raiserror
Msg 50000, Level 18, State 1, Procedure Archive_Session, Line 7
this is a raised error

这似乎是一种更干净/更快的输入方式。 - JDPeckham

5

这似乎是很多代码,但我找到的最佳方法。

    ALTER PROCEDURE Procedure
    AS

    BEGIN TRY
        EXEC AnotherProcedure
    END TRY
    BEGIN CATCH
        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;

        SELECT 
            @ErrorMessage = ERROR_MESSAGE(),
            @ErrorSeverity = ERROR_SEVERITY(),
            @ErrorState = ERROR_STATE();

        RAISERROR (@ErrorMessage, -- Message text.
                   @ErrorSeverity, -- Severity.
                   @ErrorState -- State.
                   );
        RETURN --this forces it out
    END CATCH

--Stuff here that you do not want to execute if the above failed.    

    END --end procedure

1

这是因为你没有使用BEGINEND语句。你不应该看到任何打印信息或错误提示,只有Statement Completed(或类似的内容)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接