Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/75.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 仅提交可能回滚的事务内部所做的特定更改_Sql_Sql Server 2005_Transactions_Locking_Transaction Isolation - Fatal编程技术网

Sql 仅提交可能回滚的事务内部所做的特定更改

Sql 仅提交可能回滚的事务内部所做的特定更改,sql,sql-server-2005,transactions,locking,transaction-isolation,Sql,Sql Server 2005,Transactions,Locking,Transaction Isolation,这是对原始问题的重要编辑,使其更加简洁,涵盖了现有答案提出的要点 是否可以在一个事务中对多个表进行多次更改,并仅回滚部分更改 在下面的TSQL中,我不希望“myLogSP”所做的任何更改被回滚。但各种MyBusinessSP所做的所有更改都应该在必要时回滚 BEGIN TRANSACTION EXEC myLogSP EXEC @err = myBusinessSPa IF (@err <> 0) BEGIN ROLLBACK TRANSACTION

这是对原始问题的重要编辑,使其更加简洁,涵盖了现有答案提出的要点

是否可以在一个事务中对多个表进行多次更改,并仅回滚部分更改

在下面的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
EXEC@err=myBusinessSPa
如果(@err 0)开始回滚事务返回-1结束
执行myLogSP
EXEC@err=myBusinessSPb
如果(@err 0)开始回滚事务返回-1结束
执行myLogSP
EXEC@err=myBusinessSPc
如果(@err 0)开始回滚事务返回-1结束
执行myLogSP
提交事务
返回0
顺序很重要,myLogSPs必须在myBusinessSPs之间和之后进行(myLogSPs接收myBusinessSPs所做的更改)

同样重要的是,所有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表现得好像它们不是事务的一部分一样。这只是一个不方便的事实,它们恰好位于一个内部(因为需要在MyBusinessSP之间调用)

编辑:


最后的答案是“不”,唯一的选择是重新设计代码。要么使用表变量进行日志记录(因为变量不会回滚),要么重新设计业务逻辑以不需要事务…

将日志插入移到事务之外不是一个简单的方法吗

我没有关于表锁的答案,我想你已经有了答案,因为标识列可能会回滚,所以将有一个表锁。

使用,例如

开始交易
执行myLogSP
保存事务保存点A
EXEC@err=myBusinessSPa
如果(@err 0)开始
回滚事务保存点A
犯罪
返回-1
结束
执行myLogSP
保存事务保存点B
EXEC@err=myBusinessSPb
如果(@err 0)开始
回滚事务保存点B
犯罪
返回-1
结束
执行myLogSP
保存事务savepointC
EXEC@err=myBusinessSPc
如果(@err 0)开始
回滚事务savepointC
犯罪
返回-1
结束
执行myLogSP
提交事务

编辑

根据目前提供的信息(以及我对它的理解),您似乎必须重新设计您的日志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
开始交易
保存事务保存点A
EXEC@err=myBusinessSPa
如果(@err 0)开始
回滚事务保存点A
EXEC myLogSPA--尝试调用myBusinessSPa/失败
犯罪
返回-1
结束
保存事务保存点B
EXEC@err=myBusinessSPb
如果(@err 0)开始
回滚事务保存点B
EXEC myLogSPA——对myBusinessSPa的调用最初成功了
EXEC myLogSPB--尝试调用myBusinessSPb/失败
犯罪
返回-1
结束
保存事务savepointC
EXEC@err=myBusinessSPc
如果(@err 0)开始
回滚事务savepointC
EXEC myLogSPA——对myBusinessSPa的调用最初成功了
EXEC myLogSPB——对myBusinessSPb的调用最初成功
EXEC myLogSPC--尝试调用myBusinessSPc/调用失败
犯罪
返回-1
结束
EXEC myLogSPA——对myBusinessSPa的调用成功
EXEC myLogSPB——对myBusinessSPb的调用成功
EXEC myLogSPC--对myBusinessSPc的调用成功
提交事务

在第一次插入后将BEGIN TRANSACTION语句移动到。

使用and。

我们很幸运地将日志项放入表变量中,然后在提交或回滚后插入到实际表中

如果您不在SQL Server 2008上,请尝试此方法。这是一个混乱的解决办法,但它应该会起作用。#temp表和table变量必须使用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

您需要基本上跳出当前上下文。有几种方法可以做到这一点。其中一个(我从未尝试过)是调用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
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中,该事务t2执行日志表更新和t1(业务表更新),而不进行任何回滚。例如:

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
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
开始事务t2
结束事务t2
创建过程p1
作为
开始事务t1
结束事务t1

我相信当您在存储过程中回滚t1时,调用事务t2将不受影响。

我认为您应该删除else语句,否则在回滚的情况下将永远不会提交第一次插入。根据我的经验,回滚事务将回滚所有打开的事务,不只是嵌套事务的当前级别…不,不是那些使用命名事务或保存点的情况,然后它将回滚到您命名的事务或保存点。我已进行了编辑,更好地解释了我需要的内容。感谢您提供有关命名事务和保存点的信息,我相信我很快就会使用它。但我认为这并不能解决我的问题…这个问题变得愚蠢地长,我应该删除t吗