【发布时间】:2019-05-29 23:24:28
【问题描述】:
我在 SQL Server 2017 中遇到了一个过程,该过程在 try-catch 块内有一个事务。它不是嵌套的,只是使用游标填充和循环的标识表。所以try-catch 在一个循环中,调用了其他一些过程。有时该过程会因违反约束错误而失败,并且在内部异常之前保存成功的任何内容都很好。然后我在 catch 子句中遇到了 commit。这让我想知道,我写了这段代码:
DECLARE @Table TABLE (ID INT NOT NULL PRIMARY KEY)
DECLARE @Input TABLE (ID INT)
INSERT INTO @Input
VALUES (1), (1), (2), (NULL), (3)
DECLARE @Output TABLE (ID INT)
--SET XACT_ABORT OFF
DECLARE @ID int
DECLARE [Sequence] CURSOR LOCAL FAST_FORWARD FOR
SELECT ID FROM @Input
OPEN [Sequence]
FETCH NEXT FROM [Sequence] INTO @ID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRAN
DECLARE @Msg nvarchar(max) = 'Inserting ''' + TRY_CAST(@ID as varchar(11)) + ''''
RAISERROR (@Msg, 0, 0) WITH NOWAIT
-- Order is important
--INSERT INTO @Table VALUES (@ID)
INSERT INTO @Output VALUES (@ID)
INSERT INTO @Table VALUES (@ID)
COMMIT TRAN
END TRY
BEGIN CATCH
SET @Msg = 'Caught ' + CAST(ERROR_NUMBER() as varchar(11)) + ' : ' + ERROR_MESSAGE()
RAISERROR (@Msg, 1, 1) WITH NOWAIT
IF XACT_STATE() = -1
BEGIN
SET @Msg = 'Uncommitable transaction [-1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
ROLLBACK TRAN
END
IF XACT_STATE() = 1
BEGIN
SET @Msg = 'Commitable transaction [1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
COMMIT TRAN
END
END CATCH
FETCH NEXT FROM [Sequence] INTO @ID
END
SELECT * FROM @Table
SELECT * FROM @Output
所以当我尝试交换 @Output 和 @Table 插入的顺序时,我得到了不同的结果,无论 XACT_ABORT 设置为什么,或者我是否在捕捉块。我一直很确定,一切都会回滚,@Output 和 @Table 表将相等....
我在这里做错了什么?这是默认的交易行为吗?
【问题讨论】:
-
如果一切正常,你就提交。仅当事务不可提交时才尝试回滚。如果它是可提交的但抛出了错误怎么办?触发 CATCH 的错误并不总是意味着事务状态将为 -1。例如,如果您在 COMMIT TRAN 之前放置一个 SELECT 1/0,您将触发 catch 块,但事务会非常好。
标签: sql-server transactions try-catch rollback