当具有动态 SQL 的 SP 出错时,触发器内部的 Catch 块不会被命中 - 导致较大的事务失败

Ros*_*ush 4 trigger sql-server dynamic-sql transaction-log

我不喜欢触发器和动态 sql,但是,我正在使用的东西需要两者。

CREATE TRIGGER [dbo].[GenerateDynamicFormItemViews] ON [dbo].[tblFormItems] AFTER INSERT, UPDATE
AS
BEGIN   
    SET NOCOUNT ON;
    DECLARE @DatabaseName NVARCHAR(100)
    DECLARE @FormItemID INT

    DECLARE db_cursor CURSOR FOR
    SELECT SourceID,FormItemID from Inserted

    OPEN db_cursor  
    FETCH NEXT FROM db_cursor INTO @DatabaseName, @FormItemID

    WHILE @@FETCH_STATUS = 0  
    BEGIN  

        BEGIN TRY    
            EXEC spAddEditFormItemView @FormItemID, @DatabaseName WITH RESULT SETS NONE
        END TRY    
        BEGIN CATCH 
            INSERT INTO AdminErrorLog(SourceID, ErrorNumber, ErrorState, ErrorSeverity, ErrorProcedure, ErrorLine, ErrorMessage, ErrorDateTime) 
            SELECT @DatabaseName, ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_PROCEDURE(), ERROR_LINE(), ERROR_MESSAGE(), GETDATE()
        END CATCH     

        FETCH NEXT FROM db_cursor INTO @DatabaseName, @FormItemID
    END

    CLOSE db_cursor  
    DEALLOCATE db_cursor
END 
Run Code Online (Sandbox Code Playgroud)

这就是正在发生的事情。

  1. AWS DMS 复制任务正在批量复制 10,000 个数据。复制的实现是一个黑匣子,但连接上似乎存在嵌套事务。
  2. EXEC spAddEditFormItemView 调用一个存储过程,该存储过程执行动态 sql 并在一条记录上出错。
  3. CATCH 块永远不会被执行
  4. 该错误永远不会进入 AdminErrorLog。
  5. AWS Batch 出错并且从未提交,整个任务失败。
  6. 在扩展事件中,捕获以下 SQL 错误。这是导致任务失败的错误。我读到了有关命令既不能处于提交或回滚状态的情况,但确实没有完全理解这个概念。

消息:当前事务无法提交,并且无法支持写入日志文件的操作。回滚事务。

严重程度:16

任何人都可以解释为什么 catch 块没有被捕获以及为什么客户端应用程序得到(我的猜测是)sql 异常并回滚所有内容。我的猜测是动态 sql 异常的处理方式不同,并且隐式触发事务正在回滚而不是捕获错误。另外,有谁知道避免传播异常的方法吗?

我敢打赌最安全的解决方案是修改触发器以将 sql 命令填充到表中,然后让 sql 代理作业查找每分钟左右运行的命令。

编辑-添加重新创建的过程:在 SSSM 中:

EXEC [spAddEditFormItemView] 30032,'VP_BENCHMARKING_V05'
Run Code Online (Sandbox Code Playgroud)

命令成功完成。

完成时间:2023-11-27T16:11:18.1762097-05:00

在触发器中,它会强制回滚并显示可怕的错误消息。

ALTER PROCEDURE [dbo].[spAddEditFormItemView] ( 
    @FormItemID int,
    @Schema nvarchar(100)
)
AS
    DECLARE @SQL NVARCHAR(MAX) = 'e2e2e2e2e'
    BEGIN TRY   
        EXEC (@SQLCommand)      
    END TRY
    BEGIN CATCH
        DECLARE @X INT
    END CATCH
Run Code Online (Sandbox Code Playgroud)

编辑:我现在可以在 SSMS 中复制。

在此输入图像描述

Dav*_*oft 5

触发器作为事务的一部分运行。因此,无论 catch 块是否运行,事务都注定会失败,并且永远无法提交到 AdminErrorLog 表。

因此 CATCH 块是否执行并不重要。但事实并非如此,因为触发器范围内的错误通常会中止批处理。请参阅 Erland Sommarskog 关于 TSQL 错误处理的经典文章: https: //www.sommarskog.se/error-handling-I.html#triggercontext

  • 我只需将触发器中的数据(FormItemID、DatabaseName、InsertTime、Status、Message)插入表中,然后让代理作业运行 spAddEditFormItemView 并更新状态(或删除行)。 (2认同)