En transaktion i SQL er en udførelsesenhed, der grupperer en eller flere opgaver. En transaktion anses for at være vellykket, hvis alle opgaver i den udføres uden fejl.
Men hvis nogen af opgaverne i en transaktion ikke udføres, mislykkes hele transaktionen. En transaktion har kun to resultater:vellykket eller mislykket.
Et praktisk scenarie
Overvej et praktisk eksempel på en pengeautomat (Automated Teller Machine). Du går til pengeautomaten, og den beder om dit kort. Den kører en forespørgsel for at kontrollere, om kortet er gyldigt eller ej. Dernæst beder den dig om din pinkode. Igen kører den en forespørgsel for at matche pinkoden. Hæveautomaten beder dig derefter om det beløb, du vil hæve, og du indtaster det beløb, du gerne vil have. Hæveautomaten udfører en anden forespørgsel for at trække dette beløb fra din konto og udbetaler derefter pengene til dig.
Hvad hvis beløbet trækkes fra din konto, og systemet går ned på grund af strømsvigt uden at udlevere sedler?
Dette er problematisk, fordi kunden får trukket pengene uden at have modtaget nogen penge. Det er her transaktioner kan være praktiske.
I tilfælde af et systemnedbrud eller enhver anden fejl, rulles alle opgaver i transaktionen tilbage. Derfor vil beløbet i tilfælde af en hæveautomat blive tilføjet tilbage til din konto, hvis du af en eller anden grund ikke er i stand til at hæve det.
Hvad er en transaktion?
I det enkleste er en ændring i en databasetabel en transaktion. Derfor er INSERT-, UPDATE- og DELETE-udsagn alle transaktionsudsagn. Når du skriver en forespørgsel, udføres en transaktion. Denne transaktion kan dog ikke tilbageføres. Vi vil se, hvordan transaktioner oprettes, forpligtes og rulles tilbage nedenfor, men lad os først oprette nogle dummy-data at arbejde med.
Forberedelse af dataene
Kør følgende script på din databaseserver.
CREATE DATABASE schooldb CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, gender VARCHAR(50) NOT NULL, age INT NOT NULL, total_score INT NOT NULL, ) INSERT INTO student VALUES (1, 'Jolly', 'Female', 20, 500), (2, 'Jon', 'Male', 22, 545), (3, 'Sara', 'Female', 25, 600), (4, 'Laura', 'Female', 18, 400), (5, 'Alan', 'Male', 20, 500)
Ovenstående SQL-script opretter en database schooldb. I denne database oprettes en tabelelev, og nogle dummy-data tilføjes til denne tabel.
Udførelse af forespørgsler uden transaktioner
Lad os udføre tre standardforespørgsler. Vi bruger ikke transaktioner i øjeblikket.
INSERT INTO student VALUES (6, 'Suzi', 'Female', 25, 395) UPDATE student SET age = 'Six' WHERE id= 6 DELETE from student WHERE id = 6
Her indsætter den første forespørgsel en elevpost i databasen. Den anden forespørgsel opdaterer elevens alder, og den tredje forespørgsel sletter den nyligt indsatte post.
Hvis du udfører ovenstående script, vil du se, at posten vil blive indsat i databasen, og så vil der opstå en fejl under udførelse af den anden forespørgsel.
Hvis du ser på den anden forespørgsel, opdaterer vi alder ved at gemme en strengværdi i alderskolonnen, som kan gemme heltalstypedata. Derfor vil der blive smidt en fejl. Den første forespørgsel vil dog stadig fuldføres med succes. Det betyder, at hvis du vælger alle posterne fra elevtabellen, vil du se en ny indsat post.
[tabel id=23 /]
Du kan se, at posten med id=6 og navnet 'Suzi' er blevet indsat i databasen. Men alderen kunne ikke opdateres, og den anden forespørgsel mislykkedes.
Hvad hvis vi ikke ønsker dette? Hvad hvis vi vil være sikre på, at enten alle forespørgslerne udføres med succes, eller at ingen af forespørgslerne udføres overhovedet? Det er her, transaktioner er nyttige.
Udførelse af forespørgsler med transaktioner
Lad os nu udføre de tre forespørgsler ovenfor inden for en transaktion.
Lad os først se, hvordan man opretter og udfører en transaktion.
Oprettelse af en transaktion
For at køre en forespørgsel/forespørgsler som en transaktion skal du blot indpakke forespørgslerne inden for søgeordene BEGIN TRANSACTION og COMMIT TRANSACTION. BEGIN TRANSACTION erklærer starten på en TRANSACTION, mens COMMIT TRANSACTION angiver, at transaktionen er gennemført.
Lad os udføre tre nye forespørgsler på den database, vi oprettede tidligere som en transaktion. Vi tilføjer en ny rekord for en ny elev med ID 7.
BEGIN TRANSACTION INSERT INTO student VALUES (7, 'Jena', 'Female', 22, 456) UPDATE student SET age = 'Twenty Three' WHERE id= 7 DELETE from student WHERE id = 7 COMMIT TRANSACTION
Når ovenstående transaktion udføres, vil der igen opstå en fejl i den anden forespørgsel, da der igen gemmes en strengtypeværdi i alderskolonnen, som kun gemmer heltalstypedata.
Men da fejlen opstår i en transaktion, vil alle de forespørgsler, der blev udført med succes, før denne fejl opstod, automatisk blive rullet tilbage. Derfor vil den første forespørgsel, som indsætter en ny elevrekord med id =7 og navnet 'Jena' også blive rullet tilbage.
Nu, hvis du vælger alle posterne fra elevtabellen, vil du se, at den nye post for 'Jena' ikke er blevet indsat.
Manuel tilbagerulning af transaktioner
Vi ved, at hvis en forespørgsel giver en fejl i en transaktion, bliver hele transaktionen, inklusive alle de allerede udførte forespørgsler, automatisk rullet tilbage. Vi kan dog også manuelt rulle tilbage en transaktion manuelt, når vi ønsker det.
For at rulle tilbage en transaktion bruges nøgleordet ROLLBACK efterfulgt af navnet på transaktionen. For at navngive en transaktion bruges følgende syntaks:
BEGIN TRANSACTION Transaction_name
Antag, at vi ønsker, at vores elevtabel ikke skal have nogen poster, der indeholder dublerede elevnavne. Vi tilføjer en rekord for en ny elev. Vi vil derefter kontrollere, om der findes en elev med et identisk navn som navnet på den nyindsatte elev i databasen. Hvis eleven med det navn ikke allerede eksisterer, forpligter vi vores transaktion. Hvis der eksisterer en elev med det navn, vil vi rulle vores transaktion tilbage. Vi vil gøre brug af betingede udsagn i vores forespørgsel.
Tag et kig på følgende transaktion:
DECLARE @NameCount int BEGIN TRANSACTION AddStudent INSERT INTO student VALUES (8, 'Jacob', 'Male', 21, 600) SELECT @NameCount = COUNT(*) FROM student WHERE name = 'Jacob' IF @NameCount > 1 BEGIN ROLLBACK TRANSACTION AddStudent PRINT 'A student with this name already exists' END ELSE BEGIN COMMIT TRANSACTION AddStudent PRINT 'New record added successfully' END
Tag et omhyggeligt kig på ovenstående script. Der sker en masse ting her.
I den første linje opretter vi en heltalstype SQL-variabel NameCount.
Dernæst begynder vi en transaktion med navnet 'AddStudent'. Du kan give et hvilket som helst navn til din transaktion.
Inde i transaktionen indsatte vi en ny rekord for en elev med id =8 og navnet 'Jacob'.
Derefter tæller vi antallet af elevposter, hvor navnet er 'Jacob', ved hjælp af COUNT-aggregatfunktionen, og gemmer resultatet i 'NameCount'-variablen.
Hvis værdien af variablen er større end 1, betyder det, at en elev med navnet 'Jacob' allerede findes i databasen. I så fald ROLLBACKER vi vores transaktion og PRINTSER en besked på skærmen om, at der allerede findes en elev med dette navn.
Hvis ikke, forpligter vi vores transaktion og viser meddelelsen 'Ny registrering blev tilføjet med succes'.
Når du kører ovenstående transaktion for første gang, vil der ikke være en elevpost med navnet 'Jacob'. Derfor vil transaktionen blive forpligtet, og følgende meddelelse vil blive udskrevet:
Prøv nu at køre følgende SQL-script på serveren:
DECLARE @NameCount int BEGIN TRANSACTION AddStudent INSERT INTO student VALUES (9, 'Jacob', 'Male', 22, 400) SELECT @NameCount = COUNT(*) FROM student WHERE name = 'Jacob' IF @NameCount > 1 BEGIN ROLLBACK TRANSACTION AddStudent PRINT 'A student with this name already exists' END ELSE BEGIN COMMIT TRANSACTION PRINT 'New record added successfully' END
Her indsætter vi igen elevrekord med id =9 og navn 'Jacob'. Da en elevpost med navnet 'Jacob' allerede findes i databasen, vil transaktionen rulle tilbage, og følgende meddelelse vil blive udskrevet:
Nyttige links
- Klasser om SQL-transaktioner