我有一些代码具有纯粹的顺序流,没有事务。 我用开始事务和提交事务将它们夹在中间
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会在出错时自动回滚我的事务!?我无法理解这一点。
答案 0 :(得分:1)
这种情况发生的唯一方法是该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块。似乎没有严格的规则,哪些错误可以捕获,哪些不可捕获。
为了说明差异,这里是您的代码可以捕获的错误
这是您的代码无法捕获的错误,与您的问题相似。
请注意,那里没有网格(通过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错误消息:
这是Postgresql上alter table z add zzz int;
的dev-rendered错误消息;顺便说一句,在Sql Server中,当它在这种类型的语句中出错时,它不会将控制权交还给你,因此你的CATCH
部分有时是有用的,有时是无用的。
答案 4 :(得分:0)
ALTER TABLE
语句中的错误是编译错误而不是运行时错误 - 因此永远不会执行发生该语句的整个批处理。我猜测GO
和BEGIN TRANSACTION
之间没有ALTER TABLE
- 因此BEGIN TRANSACTION
从未执行过,而SQL Server告诉你的是完全正确的。
尝试在GO
之后立即添加BEGIN TRANSACTION
。