为什么当xact_abort打开时,Sql Server在raiserror之后仍继续执行?

为什么当xact_abort打开时,Sql Server在raiserror之后仍继续执行?

Why does Sql Server keep executing after raiserror when xact_abort is on?

我只是对TSQL中的某些东西感到惊讶。 我以为如果打开xact_abort,调用类似

1
raiserror('Something bad happened', 16, 1);

将停止执行存储过程(或任何批处理)。

但是我的ADO.NET错误消息恰恰相反。 在异常消息中我同时收到了raiserror错误消息,以及在此之后发生的下一件事。

这是我的解决方法(无论如何,这是我的习惯),但似乎没有必要:

1
2
3
4
5
IF @somethingBadHappened
    BEGIN;
        raiserror('Something bad happened', 16, 1);
        RETURN;
    END;

文档说:

When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.

这是否意味着我必须使用显式事务?


这是By DesignTM,您可以在Connect上看到SQL Server团队对类似问题的回答:

Thank you for your feedback. By design, the XACT_ABORT set option does not impact the behavior of the RAISERROR statement. We will consider your feedback to modify this behavior for a future release of SQL Server.

是的,对于某些希望具有较高严重性的RAISERROR(例如16)与SQL执行错误相同的人来说,这是一个问题-并非如此。

解决方法只是您需要做的事情,使用显式事务对您要更改的行为没有任何影响。


如果使用try / catch块,严重性为11-19的raiserror错误号将导致执行跳转到catch块。

任何高于16的严重性都是系统错误。为了演示以下代码,我们设置了一个try / catch块并执行了一个我们认为会失败的存储过程:

假设我们有一个表[dbo]。[错误]用来保存错误
假设我们有一个存储过程[dbo]。[AssumeThisFails]当我们执行它时将失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
-- first lets build a temporary table to hold errors
IF (object_id('tempdb..#RAISERRORS') IS NULL)
 CREATE TABLE #RAISERRORS (ErrorNumber INT, ErrorMessage VARCHAR(400), ErrorSeverity INT, ErrorState INT, ErrorLine INT, ErrorProcedure VARCHAR(128));

-- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to
DECLARE @tc AS INT;
SET @tc = @@trancount;
IF (@tc = 0)
 BEGIN TRANSACTION;
ELSE
 save TRANSACTION myTransaction;

-- the code in the try block will be executed
BEGIN try
 DECLARE @return_value = '0';
 SET @return_value = '0';
 DECLARE
  @ErrorNumber AS INT,
  @ErrorMessage AS VARCHAR(400),
  @ErrorSeverity AS INT,
  @ErrorState AS INT,
  @ErrorLine AS INT,
  @ErrorProcedure AS VARCHAR(128);


 -- assume that this procedure fails...
 EXEC @return_value = [dbo].[AssumeThisFails]
 IF (@return_value <> 0)
  raiserror('This is my error message', 17, 1);

 -- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block
 IF (@tc = 0)
  commit TRANSACTION;
 RETURN(0);
END try


-- the code in the catch block will be executed on raiserror("message", 17, 1)
BEGIN catch
  SELECT
   @ErrorNumber = ERROR_NUMBER(),
   @ErrorMessage = ERROR_MESSAGE(),
   @ErrorSeverity = ERROR_SEVERITY(),
   @ErrorState = ERROR_STATE(),
   @ErrorLine = ERROR_LINE(),
   @ErrorProcedure = ERROR_PROCEDURE();

  INSERT #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
   VALUES (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);

  -- if i started the transaction
  IF (@tc = 0)
  BEGIN
   IF (XACT_STATE() <> 0)
   BEGIN
     SELECT * FROM #RAISERRORS;
    ROLLBACK TRANSACTION;
    INSERT INTO [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     SELECT * FROM #RAISERRORS;
    INSERT [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     VALUES (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
    RETURN(1);
   END
  END
  -- if i didn't start the transaction
  IF (XACT_STATE() = 1)
  BEGIN
   ROLLBACK TRANSACTION myTransaction;
   IF (object_id('tempdb..#RAISERRORS') IS NOT NULL)
    INSERT #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     VALUES (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
   ELSE
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
   RETURN(2);
  END
  ELSE IF (XACT_STATE() = -1)
  BEGIN
   ROLLBACK TRANSACTION;
   IF (object_id('tempdb..#RAISERRORS') IS NOT NULL)
    INSERT #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     VALUES (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
   ELSE
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
   RETURN(3);
  END
 END catch
END

RAISERROR()之后立即使用RETURN,它将不再执行该过程。


SET XACT_ABORT在文档中所指出的,应使用THROW语句而不是RAISERROR

两者的行为略有不同。但是,当XACT_ABORT设置为ON时,则应始终使用THROW命令。


推荐阅读

    linux打开网络命令?

    linux打开网络命令?,网络,系统,地址,工作,信息,工具,管理,命令,基础,状态,Lin

    linux命令行打开火狐?

    linux命令行打开火狐?,咨询,系统,火狐,软件,首页,浏览器,设备,官方网站,环

    linux命令行执行成功?

    linux命令行执行成功?,系统,信息,状态,服务,管理,百度,设计,灵活,代码,命令,L

    linux命令打开压缩?

    linux命令打开压缩?,系统,命令,工具,管理,电脑,文件,标准,信息,平台,格式,lin

    安卓执行linux命令行?

    安卓执行linux命令行?,系统,设备,基础,发展,标准,情况,信息,电话,号码,工具,

    linux外部命令调用?

    linux外部命令调用?,系统,软件,标准,命令,盘中,管理,外部,进程,程序,内存,lin

    linux打开文件夹命令?

    linux打开文件夹命令?,工作,系统,信息,命令,图片,文件,管理,发行,名字,名称,

    linux执行中退出命令?

    linux执行中退出命令?,档案,状态,命令,分析,数据,电脑,实时,系统,工具,编辑,l

    linux怎么调用命令行?

    linux怎么调用命令行?,系统,地址,工具,工作,首页,终端,命令,密码,信息,情况,l

    linux定时执行命令?

    linux定时执行命令?,时间,系统,服务,任务,工作,标准,情况,周期性,工具,命令,l

    linux打开控制台命令?

    linux打开控制台命令?,工作,系统,信息,地址,命令,目录,基础,工具,管理,第一,l

    linux看命令执行过程?

    linux看命令执行过程?,系统,服务,状态,软件,时间,数据,地址,命令,进程,情况,l

    linux登陆执行命令?

    linux登陆执行命令?,系统,服务,工具,地址,密码,百度,管理,检测,网络,第一,怎

    linux权限命令可执行?

    linux权限命令可执行?,系统,工具,信息,权限,数字,网络,发行,底部,代码,文件,l

    linux防火墙打开命令?

    linux防火墙打开命令?,系统,管理,密码,服务,软件,状态,工具,防火墙,情况,命

    linux远程执行多命令?

    linux远程执行多命令?,工具,服务,命令,状态,暂停,代码,底部,时间,地址,系统,L

    浏览器调用linux命令?

    浏览器调用linux命令?,系统,信息,人工智能,软件,数据,首次,地址,代码,咨询,

    py调用linux的命令?

    py调用linux的命令?,系统,代码,状态,环境,标准,工具,命令,文件,脚本,终端,lin

    linux下执行命令行?

    linux下执行命令行?,工作,系统,信息,单位,命令,基础,地址,设备,权威,标准,mv

    linux命令行执行工具?

    linux命令行执行工具?,工具,系统,网络,分析,工作,服务,状态,信息,电脑,发行,s