Commettere solo modifiche specifiche apportate all'interno di una TRANSAZIONE che può ROLLBACK
-
03-07-2019 - |
Domanda
Questa è una modifica significativa della domanda originale, che la rende più concisa e copre i punti sollevati dalle risposte esistenti ...
È possibile apportare modifiche multiple a più tabelle, all'interno di una singola transazione, e ripristinare solo alcune delle modifiche?
Nel TSQL di seguito, NON desidero nessuna delle modifiche apportate da " myLogSP " essere mai ritirato. Ma tutte le modifiche apportate dai vari myBusinessSP dovrebbero tornare indietro, se necessario.
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
L'ordine è importante, i myLogSP devono avvenire tra e dopo i myBusinessSP (i myLogSP raccolgono le modifiche apportate dai myBusinessSP)
È anche importante che tutti i myBusinessSP avvengano all'interno di una transazione per mantenere l'integrità del database e consentire il rollback di tutte le loro modifiche, se necessario.
È come se volessi che myLogSPs si comportasse come se non facessero parte della transazione. È solo un fatto scomodo che si trovino all'interno di uno (in virtù della necessità di essere chiamati tra i myBusinessSP.)
EDIT:
La risposta finale è "no", l'unica opzione è riprogettare il codice. Utilizzare le variabili di tabella per la registrazione (poiché le variabili non vengono ripristinate) o riprogettare la logica aziendale in Non richiedere transazioni ...
Soluzione
Usa SAVEPOINT
s , ad esempio
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
Modifica
Sulla base delle informazioni fornite finora (e della mia comprensione) sembra che dovrai riprogettare i tuoi SP di registrazione, o per utilizzare le variabili, o per utilizzare i file o per consentire loro di eseguire "dopo il fatto 'come segue:
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
Altri suggerimenti
Devi sostanzialmente saltare fuori dal contesto attuale. Ci sono un paio di modi per farlo. Uno (che non ho mai provato) è chiamare il CLR per fare l'inserimento.
Forse un modo migliore è usare il fatto che le variabili della tabella non sono influenzate dalla transazione. Ad esempio:
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
Abbiamo avuto fortuna con l'inserimento delle voci di registro nelle variabili di tabella e l'inserimento nelle tabelle reali dopo il commit o il rollback.
OK se non sei su SQL Server 2008, prova questo metodo. È disordinato e una soluzione alternativa, ma dovrebbe funzionare. La tabella #temp e la variabile table dovrebbero essere configurate con la struttura di ciò che viene restituito dallo 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
Non sarebbe semplice spostare l'inserimento del registro all'esterno della transazione?
Non ho davvero una risposta per te per il blocco della tabella, penso che tu abbia già la risposta, ci sarà avere per essere un blocco della tabella perché il la colonna identità può tornare indietro.
sposta l'istruzione BEGIN TRANSACTION dopo il primo inserimento.
Forse potresti inserire gli inserimenti / aggiornamenti nelle tabelle di business nella loro transazione atomica t1 e racchiudere ciascuna di queste transazioni in un'altra transazione t2 che esegue l'aggiornamento della tabella di registro e t1 (gli aggiornamenti della tabella di business) senza rollback. Ad esempio:
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
Ritengo che quando si esegue il rollback di t1 nella procedura memorizzata, la transazione chiamante t2 non viene modificata.