你能检测到INSERT-EXEC场景吗?

时间:2011-07-07 14:14:08

标签: sql sql-server error-handling raiserror

是否可以检测INSERT-EXEC语句是否正在调用当前存储过程?

是的,我知道我们可能不想再使用INSERT-EXEC声明......这不是我要问的问题。

我使用INSERT-EXEC的原因是因为我希望能够促进存储过程的重用,而不是一直重写相同的SQL。

这就是我关心的原因:
INSERT-EXEC方案下,一旦请求ROLLBACK,原始错误消息就会丢失。因此,任何创建的记录现在都将成为孤儿。

示例:

ALTER PROCEDURE [dbo].[spa_DoSomething]
(
    @SomeKey    INT,
    @CreatedBy  NVARCHAR(50)
)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        BEGIN TRANSACTION

        -- SQL runs and throws an error of some kind.

        COMMIT TRAN
    END TRY
    BEGIN CATCH

        IF @@TRANCOUNT > 0
            ROLLBACK TRAN

        -- If this procedure is called using an INSERT-EXEC
        -- then the original error will be lost at this point because
        -- "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." 
        -- will come-up instead of the original error.

        SET @ErrorMessage = ERROR_MESSAGE();
        SET @ErrorSeverity =    ERROR_SEVERITY();
        SET @ErrorState =       ERROR_STATE();

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)
    END CATCH

    RETURN @@Error
END

2 个答案:

答案 0 :(得分:1)

基于你不能拥有嵌套的INSERT--EXECUTE ...语句的事实,我想出了一些kludge(好吧,它是一个很大的kludge)。基本上,如果您的“问题”过程是INSERT--EXECUTE的目标,并且其本身包含INSERT--EXECUTE,则会引发错误。为了使这项工作,您必须在过程中进行(很可能毫无意义的)INSERT--EXECUTE调用,并将其包装在TRY--CATCH块中,并进行适当的处​​理。尴尬和迟钝,但如果没有其他事情出现,可能值得一试。

使用以下方法进行测试。这将创建三个过程:

IF objectproperty(object_id('dbo.Foo1'), 'isProcedure') = 1
    DROP PROCEDURE dbo.Foo1
IF objectproperty(object_id('dbo.Foo2'), 'isProcedure') = 1
    DROP PROCEDURE dbo.Foo2
IF objectproperty(object_id('dbo.Foo3'), 'isProcedure') = 1
    DROP PROCEDURE dbo.Foo3

GO
--  Returns a simple data set
CREATE PROCEDURE Foo1

AS

    SET NOCOUNT on

    SELECT name
     from sys.databases

GO
--  Calls Foo1, loads data into a local temp table, then returns those contents
CREATE PROCEDURE Foo2

AS

    SET NOCOUNT on

    CREATE TABLE #Temp (DBName sysname not null)

    BEGIN TRY
        INSERT #Temp (DBName)
         EXECUTE Foo1
    END TRY

    BEGIN CATCH
        IF ERROR_NUMBER() = 8164
            PRINT 'Nested INSERT EXECUTE'
        ELSE
            PRINT 'Unanticipated err: ' + cast(ERROR_NUMBER() as varchar(10))
    END CATCH


    SELECT *
     from #Temp

GO
--  Calls Foo2, loads data into a local temp table, then returns those contents
CREATE PROCEDURE Foo3

AS

    SET NOCOUNT on

    CREATE TABLE #Temp2 (DBName sysname not null)

    INSERT #Temp2 (DBName)
     EXECUTE Foo2

    SELECT *
     from #Temp2
GO

EXECUTE Foo1将返回“基础”数据集。

EXECUTE Foo2将调用Foo1,将数据加载到临时表中,然后返回该表的内容。

EXECUTE Foo3尝试做与Foo2相同的事情,但它调用Foo2。这会导致嵌套INSERT--EXECUTE错误,由Foo2的TRY--CATCH检测并处理。

答案 1 :(得分:0)

也许@@ NESTLEVEL可以提供帮助:

http://msdn.microsoft.com/en-us/library/ms187371.aspx