T-SQL无法回滚

时间:2011-08-10 12:18:14

标签: sql tsql sql-server-2008

我有一些代码具有纯粹的顺序流,没有事务。 我用开始事务和提交事务将它们夹在中间

begin transaction

......--My code here......
......
......--code to create Table1
......
ALTER TABLE [dbo].[Table1]  WITH CHECK ADD  CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2])
REFERENCES [dbo].[Table2] ([field3], [field4])
GO
....
......--End of My code here......


rollback transaction
commit transaction

当我运行脚本直到管理工作室中的“回滚事务”之上时,如果发生一个简单的错误,例如除零,我运行“回滚事务”,所有更改都会回滚而没有问题。

但是如果alter table语句因Table2不存在而失败,则会触发更多错误。

  

Msg 1767,Level 16,State 0,Line 2   外键'FK_Constraint引用无效表'dbo.Table2'。

     

Msg 1750,Level 16,State 0,Line 2   无法创建约束。查看以前的错误。

     

Msg 1767,Level 16,State 0,Line 2   外键'FK_xxxxxx'引用无效表'Table1'。

当我运行“回滚事务”时,我收到此错误消息“ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION”。这很愚蠢,因为我在顶部有一个开始交易!

请告诉我出了什么问题。任何帮助将非常感激。使用SQL-Server 2008。

修改

我添加了

SELECT @@TRANCOUNT;

在“ALTER TABLE [dbo]之前和之后。[Table1] with CHECK ADD CONSTRAINT”

....
SELECT @@TRANCOUNT;
ALTER TABLE [dbo].[Table1]  WITH CHECK ADD  CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2]) REFERENCES [dbo].[Table2] ([field3], [field4])
GO 
SELECT @@TRANCOUNT;
....

结果分别为1和0。 alter table会在出错时自动回滚我的事务!?我无法理解这一点。

5 个答案:

答案 0 :(得分:1)

这种情况发生的唯一方法是该SPID中没有打开的交易。

就是这样。并且没有开放交易的唯一方法是:

  • 在旧事务提交或回滚后,您从未启动过新事务
  • 你还有一个你没有注意到的提交或回滚
  • 某些东西杀死了你的连接或强制从你的spid外部回滚(比如来自另一个会话的kill命令)

您没有提供太多代码。您的查询中是否有任何错误捕获或任何其他条件逻辑未显示?

答案 1 :(得分:1)

据我所知,ALTER TABLE命令将创建自己的新事务,当它失败时,将回滚该事务。 proc中的单个回滚将导致该proc中的所有打开事务被回滚。因此,您会看到错误,因为ALTER TABLE语句的失败会在您尝试执行此操作之前隐式回滚您的事务。

您可以通过检查代码中的@TRANCOUNT轻松确认这一点,并且只在非零时调用回滚

答案 2 :(得分:1)

我认为对于使用DDL错误严重性处理的Sql Server处理,你无能为力,其中一些是由Sql Server本身自动处理(例如强制回滚事务)。

你可以做的就是让你的脚本代码应对它并为脚本用户提供描述性错误。

一个例子:

--  drop table thetransformersmorethanmeetstheeye
--  select * from thetransformersmorethanmeetstheeye



--  first batch begins here         

    begin tran

    create table thetransformersmorethanmeetstheeye(i int); -- non-erring if not yet existing

    -- even there's an error here, @@ERROR will be 0 on next batch
    ALTER TABLE [dbo].[Table1]  WITH CHECK ADD  CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2])
    REFERENCES [dbo].[Table2] ([field3], [field4]);             

go  -- first batch ends here



--  second batch begins here

    if @@TRANCOUNT > 0 begin        
        PRINT 'I have a control here if things needed be committed or rolled back';

        -- @@ERROR is always zero here, even there's an error before the GO batch. 
        -- @@ERROR cannot span two batches, it's always gets reset to zero on next batch
        PRINT @@ERROR; 


        -- But you can choose whether to COMMIT or ROLLBACK non-erring things here
        -- COMMIT TRAN;
        -- ROLLBACK TRAN;

    end
    else if @@TRANCOUNT = 0 begin
        PRINT 'Sql Server automatically rollback the transaction. Nothing can do about it';
    end
    else begin
        PRINT 'Anomaly occured, @@TRANCOUNT cannot be -1, report this to Microsoft!';
    end

--  second batch implicitly ends here   

答案 3 :(得分:0)

鉴于此:

create table z
(
i int identity(1,1) not null,
zzz int not null
);

当您尝试以下操作时..

begin try

    begin transaction

    alter table z drop column aaa;

    commit tran;

end try
begin catch 

    print 'hello';
    SELECT
        ERROR_NUMBER() as ErrorNumber,
        ERROR_MESSAGE() as ErrorMessage;

    IF (XACT_STATE()) = -1
    BEGIN
        PRINT
            N'The transaction is in an uncommittable state. ' +
            'Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

end catch

print 'reached';

..可以捕获错误:

ErrorNumber ErrorMessage
4924    ALTER TABLE DROP COLUMN failed because column 'aaa' does not exist in table 'z'.

但是尝试将alter table z drop column aaa;更改为alter table z add zzz int;,Sql Server可以捕获错误..

  

每个表中的列名必须是唯一的。表中的列名称为“zzz”   'z'被指定不止一次。

..但不会将控制权退回给你,不会触发CATCH块。似乎没有严格的规则,哪些错误可以捕获,哪些不可捕获。

为了说明差异,这里是您的代码可以捕获的错误

This is fine, the control is yielded back to you, you can perform other things when you CATCH the error

这是您的代码无法捕获的错误,与您的问题相似

The error was caught by Sql Server but won't yield back the control to you. Consequently, you cannot perform other things, your CATCH section will be rendered useless

请注意,那里没有网格(通过SELECT ERROR_NUMBER() as ErrorNumber, ERROR_MESSAGE() as ErrorMessage;)。这意味着,在检测到异常后,Sql Server没有向您返回控件。

也许您可以在此处查看可能有用的其他详细信息:http://msdn.microsoft.com/en-us/library/ms179296.aspx

请参阅此指南以了解错误处理@@ERROR and/or TRY - CATCH


顺便说一句,在Postgresql上,你的代码可以捕获所有类型的DDL错误。

do $$


begin

    -- alter table z drop column aaa;
    alter table z add zzz int;


exception when others then 

    raise notice 'The transaction is in an uncommittable state. '
                     'Transaction was rolled back';

    raise notice 'Yo this is good! --> % %', SQLERRM, SQLSTATE;
end;


$$ language 'plpgsql';

这是Postgresql上alter table z drop column aaa;的dev-rendered错误消息:

enter image description here

这是Postgresql上alter table z add zzz int;的dev-rendered错误消息;顺便说一句,在Sql Server中,当它在这种类型的语句中出错时,它不会将控制权交还给你,因此你的CATCH部分有时是有用的,有时是无用的。

in Sql Server, control execution won't be yielded back to you when there's an exception on adding of column

答案 4 :(得分:0)

ALTER TABLE语句中的错误是编译错误而不是运行时错误 - 因此永远不会执行发生该语句的整个批处理。我猜测GOBEGIN TRANSACTION之间没有ALTER TABLE - 因此BEGIN TRANSACTION从未执行过,而SQL Server告诉你的是完全正确的。

尝试在GO之后立即添加BEGIN TRANSACTION