【问题标题】:Transactions in while loop in SQL ServerSQL Server 中的 while 循环中的事务
【发布时间】:2017-12-19 15:02:56
【问题描述】:

回滚时出错:

消息 6401,级别 16,状态 1,过程 our_trigger,第 76 行
无法回滚 t1。未找到该名称的事务或保存点。

这是触发器中的 SQL 代码

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [DOCSADM].[our_trigger]
ON [DOCSADM].[PROFILE]
FOR UPDATE, INSERT
AS
    DECLARE @DocSystemId AS INTEGER
    DECLARE @docNumber AS INTEGER
    DECLARE @lastUsedSystemId AS INTEGER
    DECLARE @itemType AS VARCHAR(1) --dm10 
    DECLARE @manualBarcode AS VARCHAR (50)
    DECLARE @oldBarcodeFK AS INTEGER
    --user typed barcode, it must be inserted/UPDATED into pd_barcode table
    DECLARE @COUNTEXISTINGBARCODE AS INTEGER

    SET nocount ON;

    BEGIN
        DECLARE activity_cursor CURSOR local FOR
            SELECT 
                system_id, docnumber,
                a_doc_barcode, pd_doc_barcode, item_type
            FROM  
                inserted

        --find the last used systemid  
        SELECT @lastUsedSystemId = lastkey
        FROM docsadm.seq_systemkey

        OPEN activity_cursor

        FETCH next FROM activity_cursor INTO @DocSystemId, @docNumber, @manualBarcode, @oldBarcodeFK, @itemtype

        WHILE (@@fetch_status <> -1)
        BEGIN
            IF (( @itemType = 'M' OR @itemType = 'P' ))
                --FIND IF IT EXISTS ALREADY A BARCODE
                SELECT @COUNTEXISTINGBARCODE = COUNT(*)
                FROM docsadm.pd_barcode
                WHERE pd_barcode = @manualBarcode

                IF (@COUNTEXISTINGBARCODE = 0)-- THERE IS NO EXISTING BARCODE
                     DECLARE @barcodeSystemId AS INTEGER = 0

                BEGIN TRANSACTION t1
                BEGIN TRY
                    -- get next sys id    
                    EXECUTE [DOCSADM].[Sp_nextkey] 'SYSTEMKEY'

                    SELECT @barcodeSystemId = lastkey
                    FROM docsadm.seq_systemkey

                    INSERT INTO docsadm.pd_barcode
                    VALUES (@manualBarcode, @barcodeSystemId, 'D', NULL, NULL, 'Y', NULL, NULL)

                    UPDATE docsadm.profile
                    SET pd_doc_barcode = @barcodeSystemId
                    WHERE docnumber = @docNumber

                    COMMIT TRANSACTION t1
                END TRY
                BEGIN CATCH
                    ROLLBACK TRANSACTION t1
                END CATCH
            END

            IF (@COUNTEXISTINGBARCODE <> 0)
               --YES THERE IS AT LEAST ONE BARCODE
            BEGIN
                SELECT TOP 1 @barcodeSystemId = system_id
                FROM docsadm.pd_barcode
                WHERE pd_barcode = @manualBarcode

                BEGIN TRANSACTION t1
                BEGIN TRY
                    --update profile's new barcode reference
                    UPDATE docsadm.profile
                    SET pd_doc_barcode = @barcodeSystemId
                    WHERE docnumber = @docNumber

                    UPDATE docsadm.pd_barcode
                    SET pd_doc_bcode_used = 'Y'
                    WHERE system_id = @barcodeSystemId

                    IF (@oldBarcodeFK <> 0)
                    BEGIN
                        --update old barcode as not used!
                        UPDATE docsadm.pd_barcode
                        SET pd_doc_bcode_used = 'N'
                        WHERE system_id = @oldBarcodeFK
                    END

                    COMMIT TRANSACTION t1
                END TRY
                BEGIN CATCH
                    ROLLBACK TRANSACTION t1
                END CATCH
                END

                FETCH next FROM activity_cursor INTO @DocSystemId, @docNumber, @manualBarcode, @oldBarcodeFK, @itemtype
            END

            CLOSE activity_cursor
            DEALLOCATE activity_cursor
      END

错误来自这里

  BEGIN CATCH
      ROLLBACK TRANSACTION t1
  END CATCH

我试图保存 trans 但我得到了同样的错误。我也在循环之前开始了一个 trans 但消息发生了。

【问题讨论】:

  • 天哪! 触发器应该非常灵活和快速 - 它应该最明确不包含游标在性能方面是魔鬼......
  • 另外,您已经在事务的上下文中运行(保护导致触发器触发的UPDATEINSERT 的事务)。你不应该创建“嵌套”事务,如果你想回滚原始的INSERT/UPDATE,你应该只创建ROLLBACK——绝对不是在循环中。
  • 是的,这需要完全 100% 重写。嵌套事务是一个神话。他们根本不做看起来应该做的事情。触发器中的事务表明某些事情非常非常错误。从这里看来,这是在游标中完成的唯一真正原因是因为 sp_getNetKey 过程可能需要访问,因为这种类型的密钥生成充满了错误。
  • 你说你现在不需要时间来改变它。我想问你什么时候安排时间来解决这个问题,因为它非常需要时间。
  • 快速而肮脏的解决方案是摆脱触发器中的所有这些事务。但是你真的需要安排一些时间来解决这个问题,因为它很糟糕。

标签: sql-server triggers transactions


【解决方案1】:

此错误表示您尝试提交或回滚一个不存在的事务。因此,在 ROLLBACK 之前检查是否存在未提交的事务。像这样

IF @@TRANCOUNT>0
ROLLBACK TRANSACTION

【讨论】: