Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come risolvere "La richiesta COMMIT TRANSACTION non ha corrispondente BEGIN TRANSACTION" in SQL Server

Se ricevi il messaggio di errore 3902, livello 16, che dice "La richiesta COMMIT TRANSACTION non ha una corrispondente BEGIN TRANSACTION", probabilmente è perché hai un COMMIT randagio dichiarazione.

Potresti ottenerlo a causa dell'implementazione della gestione degli errori e della dimenticanza di aver già eseguito il commit o il rollback della transazione in un'altra parte del codice.

Esempio di errore

Ecco un semplice esempio per dimostrare l'errore:

SELECT ProductName, ProductPrice FROM Products;
COMMIT TRANSACTION;

Risultato:

(7 rows affected)
Msg 3902, Level 16, State 1, Line 2
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Ciò si verificherà se il tuo SET IMPLICIT_TRANSACTIONS è OFF . Vedi sotto per cosa succede quando SET IMPLICIT_TRANSACTIONS è ON .

Esempio di errore dovuto alla gestione degli errori

Potresti ottenerlo a causa dell'implementazione della gestione degli errori e della dimenticanza di aver già eseguito il commit o il rollback della transazione in un'altra parte del codice.

Ad esempio:

BEGIN TRANSACTION
    BEGIN TRY 

        INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
        VALUES ( 5006, SYSDATETIME(), 1006 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 1, 1, 20, 25.99 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 2, 7, 120, 9.99 );

        COMMIT TRANSACTION;

    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH
COMMIT TRANSACTION;

Risultato:

(1 row affected)
(1 row affected)
(1 row affected)
Msg 3902, Level 16, State 1, Line 20
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

In questo caso, avevo già COMMIT TRANSACTION nel TRY bloccare. Quindi per il momento il secondo COMMIT TRANSACTION è stato rilevato, la transazione era già stata salvata.

Vedremmo lo stesso anche se la transazione avesse riscontrato un errore e fosse stato annullato. Un rollback interromperà la transazione e, pertanto, nessun ulteriore COMMIT le dichiarazioni sono obbligatorie.

Quindi, per risolvere questo problema, rimuoveremmo semplicemente l'ultima COMMIT TRANSACTION e il codice della transazione sarebbe simile a questo:

BEGIN TRANSACTION
    BEGIN TRY 

        INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
        VALUES ( 5006, SYSDATETIME(), 1006 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 1, 1, 20, 25.99 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 2, 7, 120, 9.99 );

        COMMIT TRANSACTION;
        
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH

Transazioni implicite

Se hai abilitato le transazioni implicite, potresti ottenere risultati diversi rispetto al primo esempio.

Se impostiamo IMPLICIT_TRANSACTIONS su ON , ecco cosa otteniamo:

SET IMPLICIT_TRANSACTIONS ON;
SELECT ProductName, ProductPrice FROM Products;
COMMIT TRANSACTION;

Risultato:

+---------------------------------+----------------+
| ProductName                     | ProductPrice   |
|---------------------------------+----------------|
| Left handed screwdriver         | 25.99          |
| Long Weight (blue)              | 14.75          |
| Long Weight (green)             | 11.99          |
| Sledge Hammer                   | 33.49          |
| Chainsaw                        | 245.00         |
| Straw Dog Box                   | 55.99          |
| Bottomless Coffee Mugs (4 Pack) | 9.99           |
+---------------------------------+----------------+
(7 rows affected)

Non si verifica alcun errore.

Questo perché alcune istruzioni T-SQL avviano automaticamente una transazione quando vengono eseguite. È come se fossero preceduti da un invisibile BEGIN TRANSACTION dichiarazione.

Quando IMPLICIT_TRANSACTIONS è OFF , queste affermazioni vengono automaticamente salvate. È come se gli succedesse un invisibile COMMIT TRANSACTION dichiarazione. In questo scenario, la transazione è in modalità autocommit.

Quando IMPLICIT_TRANSACTIONS è ON , non esiste un COMMIT TRANSACTION invisibile dichiarazione. Queste istruzioni sono ancora avviate da un invisibile BEGIN TRANSACTION , ma devono essere interrotti in modo esplicito.

Una transazione implicita rimane in corso fino a quando non viene eseguito il commit o il rollback esplicito.

Pertanto, in questo esempio, il nostro randagio COMMIT TRANSACTION era effettivamente necessaria per terminare la transazione implicita.