T-SQL无法回滚

T-SQL无法回滚,sql,tsql,sql-server-2008,Sql,Tsql,Sql Server 2008,我有一些代码,它有一个纯粹的顺序流,没有事务。 我用begin事务和commit事务将它们夹在一起 begin transaction ......--My code here...... ...... ......--code to create Table1 ...... ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2]) REFEREN

我有一些代码,它有一个纯粹的顺序流,没有事务。 我用begin事务和commit事务将它们夹在一起

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
当我在ManagementStudio中运行脚本直到刚好在“回滚事务”上方时,如果发生一个简单的错误,例如被零除,我将运行“回滚事务”,所有更改都将毫无问题地回滚

但是,如果ALTERTABLE语句由于Table2不存在而失败,则会触发进一步的错误

信息1767,16级,状态0,第2行 外键“FK_约束”引用了无效的表“dbo.Table2”

Msg 1750,第16级,第0状态,第2行 无法创建约束。请参阅前面的错误

信息1767,16级,状态0,第2行 外键“FK_uxxxxxx”引用了无效的表“Table1”

当我运行“回滚事务”时,我得到了这样一条错误消息“回滚事务请求没有相应的开始事务”。这很愚蠢,因为我在上面有一个开始事务

请告诉我出了什么问题。任何帮助都将不胜感激。使用SQLServer2008

编辑:

我补充说

SELECT @@TRANCOUNT;
“使用CHECK ADD CONSTRAINT更改表[dbo].[Table1]之前和之后”


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

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

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

  • 在旧事务提交或回滚后,您从未启动过新事务
  • 你有另一个提交或回滚到你没有注意到的地方
  • 某些东西导致连接中断或从spid外部强制回滚(如另一个会话中的
    kill
    命令)

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

据我所知,ALTER TABLE命令将创建自己的新事务,当它失败时,将回滚该事务。进程内的一次回滚将导致该进程内所有打开的事务回滚。因此,您看到了错误,因为ALTER TABLE语句的失败是在您尝试执行之前隐式回滚事务

通过检查代码中的@TRANCOUNT,并仅在其不为零时调用rollback,您可以很容易地确认这一点,因为:

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'.
但是试着改变
altertablez,删除列aaa
更改表z添加zzz int,Sql Server可以捕获错误

每个表中的列名必须是唯一的。表中的列名“zzz” 多次指定了“z”

…但不会将控制权交还给您,不会触发捕捉块。似乎没有硬性规定什么错误是可以捕捉的,什么错误不是

为了说明区别,下面是代码可以捕获的错误

这里有一个代码无法捕获的错误,它与您的问题类似

注意这里没有网格(通过
选择ERROR\u NUMBER()作为ErrorNumber,ERROR\u MESSAGE()作为ErrorMessage;
)。这意味着,Sql Server在检测到异常后不会将控件返回给您

也许您可以在此处查看其他可能有帮助的详细信息:

有关错误处理,请参阅本指南


顺便说一下,在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';
下面是
altertablezdropcolumnaaa的dev呈现的错误消息:

以下是
altertablezaddzzzint的开发人员呈现的错误消息;顺便说一句,在Sql Server中,当它在这种类型的语句中出错时,它不会将控件返回给您,因此您的
CATCH
部分有时有用,有时无用


来自
ALTER TABLE
语句的错误是一个编译错误,而不是运行时错误,因此该语句所在的整个批永远不会执行。我猜在
开始事务
更改表
之间没有
GO
——因此
开始事务
从未执行过,SQL Server告诉您的是完全正确的


尝试在
开始事务之后立即添加一个
GO

我认为对于使用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   

听起来像是在手动运行代码的各个部分。在发生第二组错误之前,是否可能没有突出显示
开始事务
部分?是的,我正在手动运行它们。我确实在每次运行开始时突出显示了begin事务,我认为,这种DDL上的一个例外是,Sql Server会自动为您回滚事务;但真正令人失望的是,它不会将控制权交还给您,您的代码无法正常执行,例如,您的代码将无法检测事务级别(通过
@@tracount
)我撒谎了!它不起作用。您可以使用:
begin-tran;改变表格布拉赫添加布拉赫int;选择“废话”;rollback tran
它从不返回回滚,但它确实留下了一个打开的事务。@JNK:它没有留下一个打开的事务。若blah表中已经存在blah列,并且您确实存在:
begintran;改变表格布拉赫添加布拉赫int;选择“废话”;回滚传输
,它会自动回滚,尽管不是通过您的
回滚传输
代码,它是
--  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