我有两个类似的TSQL脚本,其中我有SET XACT_ABORT ON。我希望 _test 表在脚本执行后不存在,因为我故意使用包含在会引发错误的事务中的语句。
第一个脚本正确回滚_test表的创建。
SET XACT_ABORT ON
GO
BEGIN TRANSACTION
CREATE TABLE dbo._test(ID int IDENTITY(1, 1) NOT NULL)
EXEC sp_does_not_exist --raises error!
GO
IF @@TRANCOUNT > 0
COMMIT;
但是,第二个脚本不会回滚_test的创建,并且在脚本执行后该表存在:
SET XACT_ABORT ON
GO
BEGIN TRANSACTION
CREATE TABLE dbo._test(ID int IDENTITY(1, 1) NOT NULL)
EXEC sp_recompile sp_does_not_exist; --raises error!
GO
IF @@TRANCOUNT > 0
COMMIT;
为什么第二个脚本在执行后不会删除_test表?
答案 0 :(得分:0)
您尚未提及您的SQL Server版本,但由于您的脚本中唯一的区别是sp_recompile
,这似乎是一个好看的地方。在2008R2中,它具有以下逻辑:
BEGIN TRANSACTION
-- CHECK VALIDITY OF OBJECT NAME --
-- (1) Must exist in current database
-- (2) Must be a table or an executable object
select @objid = object_id(@objname, 'local')
if @objid is null OR
(ObjectProperty(@objid, 'IsTable') = 0 AND
ObjectProperty(@objid, 'IsExecuted') = 0)
begin
COMMIT TRANSACTION
raiserror(15165,-1,-1 ,@objname)
return @@error
end
所以sp_recompile
在尝试直接访问它之前检查对象是否存在,如果找不到它,则会引发严重性为-1的错误。严重性级别小于零的RAISERROR
states文档被解释为零,严重级别states的文档在严重性为零时不会引发系统错误。
确实,将RAISERROR
sp_recompile
添加到您的脚本中会显示它不会影响@@TRANCOUNT
:
SET XACT_ABORT ON
GO
BEGIN TRANSACTION
CREATE TABLE dbo._test(ID int IDENTITY(1, 1) NOT NULL)
select @@trancount as 'before raiserror'
raiserror(15165,-1,-1 ,'sp_does_not_exist')
select @@trancount as 'after raiserror'
GO
IF @@TRANCOUNT > 0
COMMIT;
引发错误之前和之后 @@TRANCOUNT
为1,因此没有任何内容可以触发回滚。但是如果你这样做,第二个SELECT
永远不会被执行,因为错误是由数据库引擎“直接”引发的:
SET XACT_ABORT ON
GO
BEGIN TRANSACTION
CREATE TABLE dbo._test(ID int IDENTITY(1, 1) NOT NULL)
select @@trancount as 'before raiserror'
exec sp_does_not_exist
select @@trancount as 'after raiserror'
GO
IF @@TRANCOUNT > 0
COMMIT;