我有两个类似的TSQL脚本,我在其中设置了XACT_ABORT。我期望_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表?
发布于 2012-11-30 21:34:47
您还没有提到您的Server版本,但是由于您的脚本中唯一的区别是sp_recompile,所以这似乎是一个很好的查看位置。在2008年R2中,它有以下逻辑:
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在尝试直接访问对象之前检查对象是否存在,如果找不到对象,则会引发严重程度的错误。严重性级别小于零的RAISERROR 州的文档被解释为零,而严重性级别州的文档在严重性为零时不会引发系统错误。
实际上,将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;https://stackoverflow.com/questions/13651216
复制相似问题