sql >> Database teknologi >  >> RDS >> Database

Selvstudium i SQL-transaktioner

I SQL bruges transaktioner til at opretholde dataintegriteten ved at sikre, at en sekvens af SQL-sætninger udføres fuldstændigt eller slet ikke.

Transaktioner administrerer sekvenser af SQL-sætninger, der skal udføres som en enkelt arbejdsenhed, så databasen aldrig indeholder resultaterne af delvise operationer.

Når en transaktion foretager flere ændringer i databasen, lykkes enten alle ændringerne, når transaktionen er forpligtet, eller alle ændringerne fortrydes, når transaktionen rulles tilbage.

Hvornår skal en transaktion bruges?

Transaktioner er altafgørende i situationer, hvor dataintegriteten ville være i fare i tilfælde af, at en af ​​en sekvens af SQL-sætninger skulle mislykkes.

Hvis du for eksempel flyttede penge fra en bankkonto til en anden, skulle du trække penge fra den ene konto og lægge dem til den anden. Du vil ikke have, at den fejler halvvejs, ellers kan penge blive debiteret fra den ene konto, men ikke krediteret den anden.

Mulige årsager til fejl kan omfatte utilstrækkelige midler, ugyldigt kontonummer, hardwarefejl osv.

Så det gør du ikke ønsker at være i en situation, hvor det forbliver sådan her:

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

Det ville være virkelig dårligt. Databasen ville have inkonsistente data, og penge ville forsvinde ud i den blå luft. Så ville banken miste en kunde (banken ville sandsynligvis miste alle sine kunder, hvis dette blev ved med at ske), og du ville miste dit job.

For at redde dit job kan du bruge en transaktion, der ville se sådan ud:

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

Du kan skrive betinget logik inde i den transaktion, der ruller transaktionen tilbage, hvis noget går galt.

For eksempel, hvis noget går galt mellem debitering af konto 1 og kreditering af konto 2, rulles hele transaktionen tilbage.

Derfor ville der kun være to mulige udfald:

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

Eller:

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

Dette er en forenklet skildring, men det er en klassisk illustration af, hvordan SQL-transaktioner fungerer. SQL-transaktioner har ACID.

Transaktionstyper

SQL-transaktioner kan køres i følgende tilstande.

Transaktionstilstand Beskrivelse
Autocommit transaktion Hvert individuelt udsagn er en transaktion.
Implicit transaktion En ny transaktion startes implicit, når den foregående transaktion er fuldført, men hver transaktion afsluttes eksplicit, typisk med en COMMIT eller ROLLBACK sætning afhængigt af DBMS.
Eksplicit transaktion Begyndte eksplicit med en linje såsom START TRANSACTION , BEGIN TRANSACTION eller lignende, afhængigt af DBMS, og eksplicit forpligtet eller rullet tilbage med de relevante udsagn.
Batch-omfattet transaktion Gælder kun for flere aktive resultatsæt (MARS). En eksplicit eller implicit transaktion, der starter under en MARS-session, bliver en batch-omfattet transaktion.

De nøjagtige tilgængelige tilstande og muligheder kan afhænge af DBMS. Denne tabel viser de tilgængelige transaktionstilstande i SQL Server.

I denne artikel er vi hovedsageligt fokuseret på eksplicitte transaktioner.

Se, hvordan implicitte transaktioner fungerer i SQL Server for en diskussion af forskellen mellem implicitte transaktioner og autocommit.

Sytnax

Følgende tabel skitserer den grundlæggende syntaks for at starte og afslutte en eksplicit transaktion i nogle af de mere populære DBMS'er.

DBMS Eksplicit transaktionssyntaks
MySQL, MariaDB, PostgreSQL Eksplicitte transaktioner starter med START TRANSACTION eller BEGIN udmelding. COMMIT begår den aktuelle transaktion, hvilket gør dens ændringer permanente. ROLLBACK ruller den aktuelle transaktion tilbage og annullerer dens ændringer.
SQLite Eksplicitte transaktioner starter med BEGIN TRANSACTION sætning og afslut med COMMIT eller ROLLBACK udmelding. Kan også slutte med END erklæring.
SQL-server Eksplicitte transaktioner starter med BEGIN TRANSACTION sætning og afslut med COMMIT eller ROLLBACK erklæring.
Oracle Eksplicitte transaktioner starter med SET TRANSACTION sætning og afslut med COMMIT eller ROLLBACK erklæring.

I mange tilfælde er visse søgeord valgfrie, når der bruges eksplicitte transaktioner. For eksempel i SQL Server og SQLite kan du blot bruge BEGIN (i stedet for BEGIN TRANSACTION ) og/eller du kan afslutte med COMMIT TRANSACTION (i modsætning til bare COMMIT ).

Der er også forskellige andre nøgleord og muligheder, som du kan angive, når du opretter en transaktion, så se din DBMS's dokumentation for den fulde syntaks.

SQL-transaktionseksempel

Her er et eksempel på en simpel transaktion i SQL Server:

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

I dette tilfælde slettes ordreoplysninger fra to tabeller. Begge udsagn behandles som én arbejdsenhed.

Vi kunne skrive betinget logik ind i vores transaktion for at få den til at rulle tilbage i tilfælde af en fejl.

Navngivning af en transaktion

Nogle DBMS'er giver dig mulighed for at angive et navn til dine transaktioner. I SQL Server kan du tilføje dit valgte navn efter BEGIN og COMMIT udsagn.

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

Eksempel 1 på tilbagerulning af SQL-transaktioner

Her er det forrige eksempel igen, men med noget ekstra kode. Den ekstra kode bruges til at rulle tilbage transaktionen i tilfælde af en fejl.:

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

TRY...CATCH sætning implementerer fejlhåndtering i SQL Server. Du kan omslutte enhver gruppe af T-SQL-sætninger i en TRY blok. Derefter, hvis der opstår en fejl i TRY blok, overføres kontrol til en anden gruppe af udsagn, der er indesluttet i en CATCH blokere.

I dette tilfælde bruger vi CATCH blokere for at rulle transaktionen tilbage. Fordi det er i CATCH blokerer, tilbagerulning sker kun, hvis der er en fejl.

Eksempel 2 på tilbagerulning af SQL-transaktioner

Lad os se nærmere på den database, vi lige har slettet rækker fra.

I det foregående eksempel slettede vi rækker fra Orders og OrderItems tabeller i følgende database:

I denne database, hver gang en kunde afgiver en ordre, indsættes en række i Orders tabel og en eller flere rækker i OrderItems bord. Antallet af rækker, der er indsat i OrderItems afhænger af, hvor mange forskellige produkter kunden bestiller.

Hvis det er en ny kunde, indsættes en ny række i Customers tabel.

I så fald skal rækker indsættes i tre tabeller.

I tilfælde af en fejl ønsker vi ikke at have en række indsat i Orders tabel, men ingen tilsvarende rækker i OrderItems bord. Det ville resultere i en ordre uden ordrevarer. Grundlæggende ønsker vi, at begge tabeller skal være fuldstændig opdaterede eller slet ikke noget.

Det var det samme, da vi slettede rækkerne. Vi ville have alle rækker slettet eller slet ingen.

I SQL Server kunne vi skrive følgende transaktion for INSERT udsagn.

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

Dette eksempel antager, at der er logik andre steder, der bestemmer, om kunden allerede eksisterer i databasen.

Kunden kunne være blevet indsat uden for denne transaktion:


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

Hvis transaktionen mislykkedes, ville kunden stadig være i databasen (men uden nogen ordrer). Applikationen skal kontrollere, om kunden allerede eksisterer, før transaktionen udføres.

SQL-transaktion med sparepunkter

Et lagringspunkt definerer et sted, hvortil en transaktion kan vende tilbage, hvis en del af transaktionen er betinget annulleret. I SQL Server angiver vi et savepoint med SAVE TRANSACTION savepoint_name (hvor savepoint_name er det navn, vi giver til lagringspunktet).

Lad os omskrive det forrige eksempel for at inkludere et lagringspunkt:


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;

Her har vi sat et sparepunkt lige efter kundens INSERT udmelding. Senere i transaktionen bruger jeg ROLLBACK sætning for at instruere transaktionen om at rulle tilbage til det lagringspunkt.

Når jeg kører den erklæring, indsættes kunden, men ingen af ​​ordreoplysningerne er indsat.

Hvis en transaktion rulles tilbage til et lagringspunkt, skal den fortsætte til fuldførelse med flere SQL-sætninger, hvis det er nødvendigt og en COMMIT TRANSACTION erklæring, eller den skal annulleres helt ved at rulle hele transaktionen tilbage.

Hvis jeg flytter ROLLBACK sætning tilbage til den forrige INSERT erklæring som denne:

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;

Dette producerer en fremmednøglekonfliktfejl. Helt konkret får jeg følgende fejl:

(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)

Dette skete, fordi, selvom ordren allerede var blevet indsat, blev den handling fortrydet, da vi rullede tilbage til lagringspunktet. Derefter fortsatte transaktionen til afslutning. Men da den stødte på den endelige ordre vare, var der ingen tilsvarende ordre (fordi den var blevet fortrudt), og vi fik fejlen.

Da jeg tjekkede databasen, blev kunden indsat, men igen, ingen af ​​ordreoplysningerne blev indsat.

Du kan referere til det samme lagringspunkt fra flere steder i transaktionen, hvis det kræves.

I praksis ville du bruge betinget programmering til at returnere transaktionen til en savepont.

Indlejrede transaktioner

Du kan også indlejre transaktioner i andre transaktioner, hvis det kræves.

Sådan:

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

Som nævnt vil den nøjagtige syntaks, du bruger til at oprette en transaktion, afhænge af dit DBMS, så tjek dit DBMS’s dokumentation for et komplet billede af dine muligheder, når du opretter transaktioner i SQL.


  1. NULL-kompleksiteter – Del 3, Manglende standardfunktioner og T-SQL-alternativer

  2. Hvordan kan jeg ændre en primær nøglebegrænsning ved hjælp af SQL-syntaks?

  3. Opdater en kolonne i MySQL

  4. Ægte vs. flydende point vs. penge