Pregunta

Esta es una edición significativa de la pregunta original, por lo que es más concisa y cubre los puntos planteados por las respuestas existentes ...

¿Es posible realizar varios cambios en varias tablas, dentro de una sola transacción, y revertir solo algunos de los cambios?

En el TSQL a continuación, NO quisiera ninguno de los cambios realizados por " myLogSP " para siempre ser revertido. Pero todos los cambios realizados por los diversos myBusinessSPs deberían revertirse si es necesario.

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

El orden es importante, los myLogSPs deben realizarse entre y después de los myBusinessSPs (los myLogSPs detectan los cambios realizados por los myBusinessSPs)

También es importante que todos los myBusinessSP se realicen dentro de una transacción para mantener la integridad de la base de datos y permitir que todos sus cambios se restituyan si es necesario.

Es como si quisiera que los myLogSP se comportaran como si no fueran parte de la transacción. Es solo un hecho inconveniente de que estén dentro de uno (en virtud de la necesidad de ser llamado entre los myBusinessSPs).

EDITAR:

La respuesta final es " no " ;, la única opción es rediseñar el código. Ya sea mediante el uso de variables de tabla para el registro (ya que las variables no se retrotraen) o rediseñar la lógica de negocios para No requerir transacciones ...

¿Fue útil?

Solución

Utilice SAVEPOINT s , por ejemplo,

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

EDIT

Según la información proporcionada hasta el momento (y mi comprensión de ello), parece que tendrá que volver a diseñar sus registros de SP, ya sea para usar variables o para usar archivos, o para permitir que se ejecuten después de la hecho 'como sigue:

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

Otros consejos

Básicamente debes saltar fuera del contexto actual. Hay un par de maneras de hacer eso. Una (que nunca he intentado) es llamar al CLR para hacer la inserción.

Quizás una mejor manera sea el hecho de que las transacciones no afecten a las variables de la tabla. Por ejemplo:

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

Tuvimos suerte con poner las entradas de registro en las variables de la tabla y luego insertarlas en las tablas reales después de la confirmación o la reversión.

Bien, si no está en SQL Server 2008, intente este método. Es desordenado y una solución alternativa, pero debería funcionar. La tabla #temp y la variable de la tabla deberían configurarse con la estructura de lo que devuelve el 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

¿No sería la forma más fácil de mover la inserción del registro fuera de la transacción?

Realmente no tengo una respuesta para usted para el bloqueo de mesa, creo que ya tiene la respuesta, habrá have para ser un bloqueo de tabla porque la columna de identidad puede retroceder.

mueve la declaración BEGIN TRANSACTION a después de la primera inserción.

Tal vez podría colocar las inserciones / actualizaciones de las tablas de negocios en su propia transacción atómica t1 y envolver cada una de estas transacciones en otra transacción t2 que ejecute la actualización de la tabla de registro y t1 (las actualizaciones de la tabla de negocios) sin ningún retroceso. Por ejemplo:

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

Creo que cuando se deshace t1 en el procedimiento almacenado, esto dejará sin efecto la transacción de llamada t2.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top