这是对原始问题的重要编辑,使其更加简洁,涵盖了现有答案所提出的观点......
是否可以在单个事务中对多个表进行多个更改,并且只回滚一些更改?
在下面的TSQL中,我不希望回滚“myLogSP”所做的任何更改。但是,如果有必要,各种myBusinessSP所做的所有更改都应该回滚。
BEGIN TRANSACTION
EXEC myLogSP
EXEC @err = myBusinessSPa
IF (@err <> 0) BEGIN ROLLBACK TRANSACTION RETURN -1 END
EXEC myLogSP
EXEC @err = myBusinessSPb
IF (@err <> 0) BEGIN ROLLBACK TRANSACTION RETURN -1 END
EXEC myLogSP
EXEC @err = myBusinessSPc
IF (@err <> 0) BEGIN ROLLBACK TRANSACTION RETURN -1 END
EXEC myLogSP
COMMIT TRANSACTION
RETURN 0
顺序很重要,myLogSP必须在myBusinessSPs之间和之后发生(myLogSPs接受myBusinessSPs所做的更改)
同样重要的是,所有myBusinessSP都在一个事务中发生以维护数据库完整性,并允许所有更改在必要时回滚。
就好像我希望myLogSP的行为就好像它们不是交易的一部分。这只是一个不方便的事实,它们碰巧在一个内部(由于需要在myBusinessSP之间调用。)
编辑:
最终答案是“不”,唯一的选择是重新设计代码。使用表变量进行日志记录(因为变量不会被回滚)或将业务逻辑重新设计为不需要事务...
答案 0 :(得分:5)
使用SAVEPOINT
s,例如
BEGIN TRANSACTION
EXEC myLogSP
SAVE TRANSACTION savepointA
EXEC @err = myBusinessSPa
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointA
COMMIT
RETURN -1
END
EXEC myLogSP
SAVE TRANSACTION savepointB
EXEC @err = myBusinessSPb
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointB
COMMIT
RETURN -1
END
EXEC myLogSP
SAVE TRANSACTION savepointC
EXEC @err = myBusinessSPc
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointC
COMMIT
RETURN -1
END
EXEC myLogSP
COMMIT TRANSACTION
修改强>
基于到目前为止提供的信息(以及我对它的理解),您似乎必须重新设计日志SP,要么使用变量,要么使用文件,或者允许它们在“之后”运行事实'如下:
BEGIN TRANSACTION
SAVE TRANSACTION savepointA
EXEC @err = myBusinessSPa
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointA
EXEC myLogSPA -- the call to myBusinessSPa was attempted/failed
COMMIT
RETURN -1
END
SAVE TRANSACTION savepointB
EXEC @err = myBusinessSPb
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointB
EXEC myLogSPA -- the call to myBusinessSPa originally succeeded
EXEC myLogSPB -- the call to myBusinessSPb was attempted/failed
COMMIT
RETURN -1
END
SAVE TRANSACTION savepointC
EXEC @err = myBusinessSPc
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointC
EXEC myLogSPA -- the call to myBusinessSPa originally succeeded
EXEC myLogSPB -- the call to myBusinessSPb originally succeeded
EXEC myLogSPC -- the call to myBusinessSPc was attempted/failed
COMMIT
RETURN -1
END
EXEC myLogSPA -- the call to myBusinessSPa succeeded
EXEC myLogSPB -- the call to myBusinessSPb succeeded
EXEC myLogSPC -- the call to myBusinessSPc succeeded
COMMIT TRANSACTION
答案 1 :(得分:2)
我们很幸运将日志条目放入表变量中,然后在提交或回滚后插入真实表。
如果您不在SQL Server 2008上,请单击确定,然后尝试此方法。这是一个混乱和一个解决方法,但它应该工作。 #temp表和表变量必须使用sp返回的结构进行设置。
create table #templog (fie1d1 int, field2 varchar(10))
declare @templog table (fie1d1 int, field2 varchar(10))
BEGIN TRANSACTION
insert into #templog
Exec my_proc
insert into @templog (fie1d1, field2)
select t.* from #templog t
left join @templog t2 on t.fie1d1 = t2.fie1d1 where t2.fie1d1 is null
insert into templog
values (1, 'test')
rollback tran
select * from #templog
select * from templog
select * from @templog
答案 2 :(得分:2)
您需要基本跳出当前上下文。有几种方法可以做到这一点。一个(我从未尝试过)是调用CLR进行插入。
或许更好的方法是使用表变量不受事务影响的事实。例如:
CREATE TABLE dbo.Test_Transactions
(
my_string VARCHAR(20) NOT NULL
)
GO
DECLARE
@tbl TABLE (my_string VARCHAR(20) NOT NULL)
BEGIN TRANSACTION
INSERT INTO dbo.Test_Transactions (my_string) VALUES ('test point one')
INSERT INTO @tbl (my_string) VALUES ('test point two')
INSERT INTO dbo.Test_Transactions (my_string) VALUES ('test point three')
ROLLBACK TRANSACTION
INSERT INTO dbo.Test_Transactions (my_string) select my_string from @tbl
SELECT * FROM dbo.Test_Transactions
SELECT * FROM @tbl
GO
答案 3 :(得分:1)
答案 4 :(得分:0)
在交易之外移动日志插入不是一件容易的事吗?
对于桌面锁定,我真的没有答案,我认为你已经有了答案, 有 是一个表锁,因为标识列可能会回滚。
答案 5 :(得分:0)
在第一次插入后移动BEGIN TRANSACTION语句。
答案 6 :(得分:0)
也许您可以将业务表的插入/更新放在它们自己的原子事务t1中,并将这些事务中的每一个包装在执行日志表更新和t1(业务表更新)的另一个事务t2中,而不进行任何回滚。例如:
BEGIN TRANSACTION t2
<insert to log>
<execute stored procedure p1>
END TRANSACTION t2
CREATE PROCEDURE p1
AS
BEGIN TRANSACTION t1
<insert to business tables>
<rollback t1 on error>
END TRANSACTION t1
我相信当你在存储过程中回滚t1时,这将使调用事务t2不受影响。