Validation des modifications spécifiques apportées à l’intérieur d’une TRANSACTION pouvant entraîner un retour en arrière

StackOverflow https://stackoverflow.com/questions/618928

Question

Ceci est une modification significative de la question initiale, la rendant plus concise et couvrant les points soulevés par les réponses existantes ...

Est-il possible d’apporter de multiples modifications à plusieurs tables, au sein d’une seule transaction, et d’annuler certaines modifications seulement?

Dans les TSQL ci-dessous, je ne souhaite aucune des modifications apportées par "myLogSP". à jamais être reculé. Cependant, toutes les modifications apportées par les différents prestataires de services myBusiness doivent être annulées si nécessaire.

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

La commande est importante, les myLogSPs doivent avoir lieu entre et après les myBusinessSPs (les myLogSP prennent en compte les modifications apportées par les myBusinessSPs)

Il est également important que tous les myBusinessSPs se produisent dans une même transaction afin de préserver l'intégrité de la base de données et de permettre, le cas échéant, l'annulation de toutes leurs modifications.

C'est comme si je voulais que les myLogSP se comportent comme s'ils ne faisaient pas partie de la transaction. C’est un fait gênant qu’ils se trouvent à l’intérieur de celui-ci (en raison de la nécessité de s’appeler entre les prestataires myBusinessSP.)

EDIT:

La réponse finale est "non", la seule option est de modifier le code. Vous pouvez soit utiliser des variables de table pour la journalisation (car les variables ne sont pas restaurées), soit redéfinir la logique métier sur Ne pas exiger de transactions ...

Était-ce utile?

La solution

Utilisez les SAVEPOINT , par exemple

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

MODIFIER

Sur la base des informations fournies à ce jour (et à ma connaissance), il apparaît que vous devrez réorganiser la journalisation de vos SP, soit utiliser des variables, soit des fichiers, soit les autoriser à s'exécuter après la fait 'comme suit:

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

Autres conseils

Vous devez fondamentalement sauter en dehors du contexte actuel. Il y a deux façons de le faire. L’une (que je n’ai jamais essayée) est d’appeler le CLR pour faire l’insertion.

Peut-être une meilleure solution consiste-t-elle toutefois à utiliser le fait que les variables de table ne sont pas affectées par la transaction. Par exemple:

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

Nous avons eu de la chance de placer les entrées du journal dans des variables de table, puis de les insérer dans les tables réelles après la validation ou la restauration.

OK si vous n’êtes pas sur SQL Server 2008, essayez cette méthode. C'est désordonné et une solution de contournement, mais cela devrait fonctionner. La table #temp et la variable de table doivent être définies avec la structure de ce qui est renvoyé par le 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

Ne serait-il pas facile de déplacer l'insertion de journal en dehors de la transaction?

Je n'ai pas vraiment de réponse pour vous pour le verrou de table, je pense que vous avez déjà la réponse, il va devoir être un verrou de table car la colonne d'identité peut être annulée.

déplacez l'instruction BEGIN TRANSACTION après la première insertion.

Peut-être pourriez-vous placer les insertions / mises à jour des tables commerciales dans leur propre transaction atomique t1 et encapsuler chacune de ces transactions dans une autre transaction t2 qui exécute la mise à jour de la table de journal et t1 (la mise à jour de la table commerciale) sans aucune restauration. Par exemple:

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

Je pense que, lorsque vous restaurez t1 dans la procédure stockée, la transaction appelante t2 restera inchangée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top