TRY块之外的约束冲突是否可以表现为异常(并停止执行)



摘要

我刚刚发现SQL Server的一个令人惊讶的行为:如果一条语句在TRY块之外执行,并且它违反了一个约束1,则执行将继续到下一条语句。

我本以为执行会停止,就像异常一样,但显然违反约束的行为并不(总是(像异常一样。但是,如果一条语句在TRY块内执行,则约束冲突的行为类似于异常,并在到达下一条语句之前停止执行流。

测试

这是我的测试设置。。。

CREATE TABLE T (
T_ID int,
CONSTRAINT T_PK PRIMARY KEY (T_ID)
);
INSERT INTO T VALUES (1);

下面,INSERT违反了主键,但仍然执行下一条语句(打印"下一个到达"(:

INSERT INTO T VALUES(1);
PRINT 'next reached';
Msg 2627, Level 14, State 1, Line 10
Violation of PRIMARY KEY constraint 'T_PK'. Cannot insert duplicate key in object 'dbo.T'. The duplicate key value is (1).
The statement has been terminated.
next reached

这与";正常的";异常(未打印"下一个到达"(:

THROW 50000, 'Some exception.', 0;
PRINT 'next reached';
Msg 50000, Level 16, State 0, Line 15
Some exception.

奇怪的是,如果我将INSERT放入TRY块中,则约束冲突开始表现为异常(注意如何既不打印"下一个到达"也不打印"最终到达"(:

BEGIN TRY
INSERT INTO T VALUES(1);
PRINT 'next reached';
END TRY
BEGIN CATCH
THROW;
END CATCH
PRINT 'end reached'
(0 rows affected)
Msg 2627, Level 14, State 1, Line 21
Violation of PRIMARY KEY constraint 'T_PK'. Cannot insert duplicate key in object 'dbo.T'. The duplicate key value is (1).

问题

有没有一种方法可以使约束冲突表现得像异常,即使没有包含在TRY块中?

动机:以上行为不同于我所知的所有支持异常的编程语言。在存储过程中实现复杂的业务逻辑时,到处使用TRY会降低代码的可读性。更重要的是,它会使它变得脆弱:将它忘在某个地方,或者依赖调用方来拥有它,然后有人独立地将它从调用方中删除,很容易使存储过程意外地继续执行,甚至可能损坏数据。


1我测试了PRIMARY KEY、UNIQUE、FOREIGN KEY和CHECK约束,它们的行为似乎都一样

---编辑---

值得一提的是,我已经为微软提出了一个建议,但我并没有屏住呼吸,它将很快(或永远(实现:

具有算术错误和违反限制的一致执行流程

解决您的"动机;部分,我强烈反对。try-transaction block有很多版本,但人们迟早会意识到它应该包含在几乎每个过程中。它不超过20行代码,所以读起来并不痛苦。以下是我使用的:

set xact_abort on -- rollsback when try-catch doesn't work, eg on table not found errors
begin try
begin transaction    -- start the transaction
...........................
commit
end try
begin catch
if @@trancount > 0 rollback
declare @rethrow nvarchar(4000)='Error >> num '+convert(nvarchar(max),error_number())+', line '+convert(nvarchar(max),error_line())+', message: '+error_message()
raiserror(@rethrow, 16, 0)
end catch

和往常一样,我推荐Erland Sommarskog的相应文章,在其中你会发现一个表,详细说明了各种错误类型组合的确切行为,xact_abort on/off,以及使用或不使用try/catch。

你还提到你想让你的客户选择是否";回滚或重试";。我对此并不熟悉,但它有一种不祥的感觉。什么是";重试";?如果您计划进行一些手动更改,然后重试,请不要忘记,如果事务仍然打开,则您的过程可能仍处于锁定状态,从而导致糟糕的情况。最好的方法是进行循环,其中事务将被回滚,重试意味着从一开始就启动它。

最新更新