sql >> Database >  >> RDS >> Database

Zelfstudie SQL-transacties

In SQL worden transacties gebruikt om de gegevensintegriteit te behouden door ervoor te zorgen dat een reeks SQL-instructies volledig of helemaal niet wordt uitgevoerd.

Transacties beheren reeksen SQL-instructies die moeten worden uitgevoerd als een enkele werkeenheid, zodat de database nooit de resultaten van gedeeltelijke bewerkingen bevat.

Wanneer een transactie meerdere wijzigingen in de database aanbrengt, slagen alle wijzigingen wanneer de transactie wordt doorgevoerd of worden alle wijzigingen ongedaan gemaakt wanneer de transactie wordt teruggedraaid.

Wanneer een transactie gebruiken?

Transacties zijn van het grootste belang in situaties waarin de gegevensintegriteit in gevaar zou komen in het geval dat een van een reeks SQL-instructies zou mislukken.

Als u bijvoorbeeld geld van de ene bankrekening naar de andere zou overmaken, moet u geld van de ene rekening aftrekken en bij de andere optellen. Je zou niet willen dat het halverwege mislukt, anders zou er geld van de ene rekening kunnen worden afgeschreven maar niet op de andere.

Mogelijke redenen voor het mislukken zijn onder meer onvoldoende saldo, een ongeldig rekeningnummer, een hardwarestoring, enz.

Dus je doet niet in een situatie willen zijn waarin het zo blijft:

Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)

Dat zou echt zijn slecht. De database zou inconsistente gegevens bevatten en geld zou in het niets verdwijnen. Dan zou de bank een klant verliezen (de bank zou waarschijnlijk al haar klanten verliezen als dit zo zou blijven) en jij zou je baan verliezen.

Om uw taak op te slaan, kunt u een transactie gebruiken die er ongeveer als volgt uitziet:

START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION 

Je zou voorwaardelijke logica in die transactie kunnen schrijven die de transactie terugdraait als er iets misgaat.

Als er bijvoorbeeld iets misgaat tussen het debiteren van rekening 1 en het crediteren van rekening 2, wordt de hele transactie teruggedraaid.

Daarom zijn er maar twee mogelijke uitkomsten:

Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)

Of:

Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)

Dit is een vereenvoudigde weergave, maar het is een klassieke illustratie van hoe SQL-transacties werken. SQL-transacties hebben ACID.

Transactietypen

SQL-transacties kunnen in de volgende modi worden uitgevoerd.

Transactiemodus Beschrijving
Transactie automatisch vastleggen Elk individueel overzicht is een transactie.
Impliciete transactie Een nieuwe transactie wordt impliciet gestart wanneer de vorige transactie is voltooid, maar elke transactie wordt expliciet voltooid, meestal met een COMMIT of ROLLBACK statement afhankelijk van het DBMS.
Expliciete transactie Expliciet begonnen met een regel zoals START TRANSACTION , BEGIN TRANSACTION of vergelijkbaar, afhankelijk van het DBMS, en expliciet vastgelegd of teruggedraaid met de relevante instructies.
Batch-scoped transactie Alleen van toepassing op meerdere actieve resultatensets (MARS). Een expliciete of impliciete transactie die begint onder een MARS-sessie, wordt een transactie met batchbereik.

De exacte beschikbare modi en opties kunnen afhankelijk zijn van het DBMS. Deze tabel geeft een overzicht van de transactiemodi die beschikbaar zijn in SQL Server.

In dit artikel richten we ons vooral op expliciete transacties.

Zie Hoe impliciete transacties werken in SQL Server voor een bespreking van het verschil tussen impliciete transacties en automatisch vastleggen.

Sytnax

De volgende tabel geeft een overzicht van de basissyntaxis voor het starten en beëindigen van een expliciete transactie in enkele van de meer populaire DBMS'en.

DBMS Expliciete transactiesyntaxis
MySQL, MariaDB, PostgreSQL Expliciete transacties beginnen met de START TRANSACTION of BEGIN uitspraak. COMMIT begaat de huidige transactie, waardoor de wijzigingen permanent worden. ROLLBACK draait de huidige transactie terug en annuleert de wijzigingen.
SQLite Expliciete transacties beginnen met de BEGIN TRANSACTION statement en eindigen met de COMMIT of ROLLBACK uitspraak. Kan ook eindigen met de END verklaring.
SQL-server Expliciete transacties beginnen met de BEGIN TRANSACTION statement en eindigen met de COMMIT of ROLLBACK verklaring.
Oracle Expliciete transacties beginnen met de SET TRANSACTION statement en eindigen met de COMMIT of ROLLBACK verklaring.

In veel gevallen zijn bepaalde trefwoorden optioneel bij het gebruik van expliciete transacties. In SQL Server en SQLite kunt u bijvoorbeeld eenvoudig BEGIN . gebruiken (in plaats van BEGIN TRANSACTION ) en/of je zou kunnen eindigen met COMMIT TRANSACTION (in tegenstelling tot gewoon COMMIT ).

Er zijn ook verschillende andere trefwoorden en opties die u kunt opgeven bij het maken van een transactie, dus raadpleeg de documentatie van uw DBMS voor de volledige syntaxis.

SQL-transactievoorbeeld

Hier is een voorbeeld van een eenvoudige transactie in SQL Server:

BEGIN TRANSACTION
    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;

In dit geval wordt de bestelinformatie uit twee tabellen verwijderd. Beide uitspraken worden als één werkeenheid behandeld.

We zouden voorwaardelijke logica in onze transactie kunnen schrijven om het terug te draaien in het geval van een fout.

Een transactie een naam geven

Bij sommige DBMS'en kunt u een naam opgeven voor uw transacties. In SQL Server kunt u de door u gekozen naam toevoegen na de BEGIN en COMMIT verklaringen.

BEGIN TRANSACTION MyTransaction
    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;

SQL-transactie terugdraaien voorbeeld 1

Hier is het vorige voorbeeld nogmaals, maar met wat extra code. De extra code wordt gebruikt om de transactie terug te draaien in het geval van een fout.:

BEGIN TRANSACTION MyTransaction

  BEGIN TRY

    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;

    COMMIT TRANSACTION MyTransaction

  END TRY

  BEGIN CATCH

      ROLLBACK TRANSACTION MyTransaction

  END CATCH

De TRY...CATCH instructie implementeert foutafhandeling in SQL Server. U kunt elke groep T-SQL-instructies insluiten in een TRY blok. Als er dan een fout optreedt in de TRY blok, wordt de controle doorgegeven aan een andere groep instructies die is ingesloten in een CATCH blok.

In dit geval gebruiken we de CATCH blok om de transactie terug te draaien. Gezien het in de CATCH . staat blok, terugdraaien vindt alleen plaats als er een fout is.

SQL-transactie terugdraaien voorbeeld 2

Laten we de database waarvan we zojuist rijen hebben verwijderd eens nader bekijken.

In het vorige voorbeeld hebben we rijen verwijderd uit de Orders en OrderItems tabellen in de volgende database:

In deze database wordt elke keer dat een klant een bestelling plaatst, een rij ingevoegd in de Orders tabel, en een of meer rijen in de OrderItems tafel. Het aantal rijen dat is ingevoegd in OrderItems hangt af van hoeveel verschillende producten de klant bestelt.

Als het een nieuwe klant is, wordt er ook een nieuwe rij ingevoegd in de Customers tafel.

In dat geval moeten rijen in drie tabellen worden ingevoegd.

In het geval van een storing, willen we niet dat er een rij wordt ingevoegd in de Orders tabel maar geen corresponderende rijen in de OrderItems tafel. Dat zou resulteren in een bestelling zonder bestelitems. Kortom, we willen dat beide tabellen volledig worden bijgewerkt of helemaal niets.

Het was hetzelfde toen we de rijen verwijderden. We wilden alle rijen verwijderen of helemaal geen.

In SQL Server kunnen we de volgende transactie schrijven voor de INSERT verklaringen.

BEGIN TRANSACTION
    BEGIN TRY 
        INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
        VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');

        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

In dit voorbeeld wordt ervan uitgegaan dat er elders logica is die bepaalt of de klant al in de database bestaat.

De klant kan buiten deze transactie zijn ingevoegd:


INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');

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

Als de transactie mislukte, zou de klant nog steeds in de database staan ​​(maar zonder bestellingen). De applicatie zou moeten controleren of de klant al bestaat voordat de transactie wordt uitgevoerd.

SQL-transactie met spaarpunten

Een savepoint definieert een locatie waarnaar een transactie kan terugkeren als een deel van de transactie voorwaardelijk wordt geannuleerd. In SQL Server specificeren we een savepoint met SAVE TRANSACTION savepoint_name (waar savepoint_name is de naam die we aan het savepoint geven).

Laten we het vorige voorbeeld herschrijven om een ​​opslagpunt op te nemen:


BEGIN TRANSACTION
    INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
    VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
    SAVE TRANSACTION StartOrder;

    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 );
    ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;

Hier hebben we een opslagpunt ingesteld direct na de klant INSERT uitspraak. Later in de transactie gebruik ik de ROLLBACK instructie om de transactie te instrueren om terug te gaan naar dat opslagpunt.

Wanneer ik die verklaring uitvoer, wordt de klant ingevoegd, maar geen van de bestelinformatie wordt ingevoegd.

Als een transactie wordt teruggedraaid naar een opslagpunt, moet deze worden voltooid met meer SQL-instructies indien nodig en een COMMIT TRANSACTION verklaring, of het moet helemaal worden geannuleerd door de hele transactie terug te draaien.

Als ik de ROLLBACK . verplaats statement terug naar de vorige INSERT verklaring, zoals deze:

BEGIN TRANSACTION
    INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
    VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
    SAVE TRANSACTION StartOrder;

    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 );
    ROLLBACK TRANSACTION StartOrder;
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;

Dit levert een conflictfout met een refererende sleutel op. In het bijzonder krijg ik de volgende foutmelding:

(1 row affected)
(1 row affected)
(1 row affected)
Msg 547, Level 16, State 0, Line 13
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_OrderItems_Orders". The conflict occurred in database "KrankyKranes", table "dbo.Orders", column 'OrderId'.
The statement has been terminated.
(1 row affected)

Dit gebeurde omdat, hoewel de bestelling al was ingevoegd, die bewerking ongedaan werd gemaakt toen we teruggingen naar het opslagpunt. Daarna ging de transactie tot voltooiing. Maar toen het het laatste bestellingsitem tegenkwam, was er geen overeenkomstige bestelling (omdat die ongedaan was gemaakt) en kregen we de foutmelding.

Toen ik de database controleer, is de klant ingevoegd, maar nogmaals, geen van de bestelinformatie is ingevoegd.

U kunt indien nodig op meerdere plaatsen in de transactie naar hetzelfde opslagpunt verwijzen.

In de praktijk zou je conditionele programmering gebruiken om de transactie terug te sturen naar een savepont.

Geneste transacties

Indien nodig kunt u transacties ook nesten in andere transacties.

Zoals dit:

BEGIN TRANSACTION Transaction1;  
    UPDATE table1 ...;
    BEGIN TRANSACTION Transaction2;
        UPDATE table2 ...;
        SELECT * from table1;
    COMMIT TRANSACTION Transaction2;
    UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;

Zoals vermeld, hangt de exacte syntaxis die u gebruikt om een ​​transactie aan te maken af ​​van uw DBMS, dus raadpleeg de documentatie van uw DBMS voor een volledig beeld van uw opties bij het maken van transacties in SQL.


  1. Archieflogboekbestemmingsdirectory instellen in Oracle Database

  2. Krijg resultatenset van orakel opgeslagen procedure

  3. Java JDBC MySQL-uitzondering:bewerking niet toegestaan ​​nadat ResultSet is gesloten

  4. KRUIS/BUITEN TOEPASSEN in MySQL