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

Implementering af fejl- og transaktionshåndtering i SQL Server

Introduktion

Uanset hvor hårdt vi prøver at designe og udvikle applikationer, vil der altid opstå fejl. Der er to generelle kategorier - syntaks eller logiske fejl kan enten være programmatiske fejl eller konsekvenser af forkert databasedesign. Ellers kan du få en fejl på grund af forkert brugerinput.

T-SQL (SQL Server-programmeringssproget) tillader håndtering af begge fejltyper. Du kan fejlsøge programmet og beslutte, hvad du skal gøre for at undgå fejl i fremtiden.

De fleste applikationer kræver, at du logger fejl, implementerer brugervenlig fejlrapportering og, når det er muligt, håndterer fejl og fortsætter applikationsudførelsen.

Brugere håndterer fejl på et sætningsniveau. Det betyder, at når du kører en batch af SQL-kommandoer, og problemet opstår i den sidste sætning, vil alt, der går forud for det problem, blive forpligtet til databasen som implicitte transaktioner. Det er måske ikke det, du ønsker.

Relationelle databaser er optimeret til udførelse af batch-sætninger. Du skal således udføre en batch af sætninger som én enhed og fejle alle sætningerne, hvis en sætning fejler. Du kan opnå dette ved at bruge transaktioner. Denne artikel vil fokusere på både fejlhåndtering og transaktioner, da disse emner er stærkt forbundet.

SQL fejlhåndtering

For at simulere undtagelser er vi nødt til at producere dem på en gentagelig måde. Lad os starte med det enkleste eksempel – division med nul:

SELECT 1/0

Outputtet beskriver den kastede fejl – Divider med nul fejl fundet . Men denne fejl blev ikke håndteret, logget eller tilpasset til at producere en brugervenlig besked.

Undtagelseshåndtering starter med at sætte sætninger, du vil udføre, i BEGIN TRY...END TRY-blokken.

SQL Server håndterer (fanger) fejl i BEGIN CATCH...END CATCH-blokken, hvor du kan indtaste brugerdefineret logik til fejllogning eller -behandling.

BEGIN CATCH-sætningen skal følge umiddelbart efter END TRY-sætningen. Udførelsen sendes derefter fra TRY-blokken til CATCH-blokken ved den første fejl.

Her kan du bestemme, hvordan du skal håndtere fejlene, om du vil logge dataene om rejste undtagelser eller oprette en brugervenlig besked.

SQL Server har indbyggede funktioner, som kan hjælpe dig med at udtrække fejldetaljer:

  • ERROR_NUMBER():Returnerer antallet af SQL-fejl.
  • ERROR_SEVERITY():Returnerer det alvorlighedsniveau, der angiver typen af ​​problem, der er stødt på, og dets niveau. Niveau 11 til 16 kan håndteres af brugeren.
  • ERROR_STATE():Returnerer fejltilstandsnummeret og giver flere detaljer om den kastede undtagelse. Du bruger fejlnummeret til at søge i Microsofts videnbase efter specifikke fejloplysninger.
  • ERROR_PROCEDURE():Returnerer navnet på proceduren eller triggeren, hvori fejlen blev rejst, eller NULL, hvis fejlen ikke opstod i proceduren eller triggeren.
  • ERROR_LINE():Returnerer linjenummeret, hvor fejlen opstod. Det kan være linjenummeret på procedurer eller triggere eller linjenummeret i batchen.
  • ERROR_MESSAGE():Returnerer teksten i fejlmeddelelsen.

Følgende eksempel viser, hvordan man håndterer fejl. Det første eksempel indeholder Division med nul fejl, mens den anden sætning er korrekt.

BEGIN TRY
   PRINT 1/0  
   SELECT 'Correct text'
END TRY
BEGIN CATCH
   SELECT ERROR_NUMBER() AS ERR_NO
   ,      ERROR_SEVERITY() AS ERR_SEV
   ,      ERROR_STATE() AS ERR_STATE
   ,      ERROR_LINE() AS ERR_LINE
   ,      ERROR_MESSAGE() AS ERR_MESSAGE
END CATCH

Hvis den anden sætning udføres uden fejlhåndtering (VÆLG 'Ret tekst'), ville den lykkes.

Da vi implementerer den tilpassede fejlhåndtering i TRY-CATCH-blokken, overføres programafviklingen til CATCH-blokken efter fejlen i den første sætning, og den anden sætning blev aldrig udført.

På denne måde kan du ændre den tekst, der er givet til brugeren, og kontrollere, hvad der sker, hvis en fejl finder sted bedre. For eksempel logger vi fejl til en logtabel for yderligere analyse.

Brug af transaktioner

Forretningslogikken kan bestemme, at indsættelsen af ​​den første sætning mislykkes, når den anden sætning mislykkes, eller at du muligvis skal gentage ændringer af den første sætning ved den anden sætningsfejl. Ved at bruge transaktioner kan du udføre en batch af udsagn som én enhed, der enten fejler eller lykkes.

Følgende eksempel viser brugen af ​​transaktioner.

Først opretter vi en tabel for at teste de lagrede data. Så bruger vi to transaktioner inde i TRY-CATCH-blokken til at simulere de ting, der sker, hvis en del af transaktionen mislykkes.

Vi vil bruge CATCH-sætningen med XACT_STATE()-sætningen. Funktionen XACT_STATE() bruges til at kontrollere, om transaktionen stadig eksisterer. I tilfælde af at transaktionen ruller tilbage automatisk, vil TILBAGETRANSAKTIONEN give en ny undtagelse.

Få et bytte ved nedenstående kode:

-- CREATE TABLE TEST_TRAN(VALS INT)

BEGIN TRY
   BEGIN TRANSACTION
       INSERT INTO TEST_TRAN(VALS) VALUES(1);
   COMMIT TRANSACTION  

   BEGIN TRANSACTION
       INSERT INTO TEST_TRAN(VALS) VALUES(2);
       INSERT INTO TEST_TRAN(VALS) VALUES('A'); 
       INSERT INTO TEST_TRAN(VALS) VALUES(3);
   COMMIT TRANSACTION
END TRY
BEGIN CATCH  
   IF XACT_STATE() > 0 ROLLBACK TRANSACTION

   SELECT ERROR_NUMBER() AS ERR_NO
   ,      ERROR_SEVERITY() AS ERR_SEV
   ,      ERROR_STATE() AS ERR_STATE
   ,      ERROR_LINE() AS ERR_LINE
   ,      ERROR_MESSAGE() AS ERR_MESSAGE

END CATCH

SELECT * FROM TEST_TRAN

-- DROP TABLE TEST_TRAN

Billedet viser værdierne i TEST_TRAN-tabellen og fejlmeddelelser:

Som du kan se, blev kun den første værdi begået. I den anden transaktion havde vi en typekonverteringsfejl i anden række. Således rullede hele partiet tilbage.

På denne måde kan du kontrollere, hvilke data der kommer ind i databasen, og hvordan batches behandles.

Generer tilpasset fejlmeddelelse i SQL

Nogle gange ønsker vi at oprette brugerdefinerede fejlmeddelelser. Normalt er de beregnet til scenarier, hvor vi ved, at der kan opstå et problem. Vi kan producere vores egne brugerdefinerede beskeder, der siger, at der er sket noget galt uden at vise tekniske detaljer. Til det bruger vi søgeordet THROW.

BEGIN TRY
   IF ( SELECT COUNT(sys.all_objects) > 1 )
	THROW ‘More than one object is ALL_OBJECTS system table’
END TRY
BEGIN CATCH
   SELECT ERROR_NUMBER() AS ERR_NO
   ,      ERROR_SEVERITY() AS ERR_SEV
   ,      ERROR_STATE() AS ERR_STATE
   ,      ERROR_LINE() AS ERR_LINE
   ,      ERROR_MESSAGE() AS ERR_MESSAGE
END CATCH

Eller vi vil gerne have et katalog over brugerdefinerede fejlmeddelelser til kategorisering og konsistens af fejlovervågning og -rapportering. SQL Server giver os mulighed for at foruddefinere fejlmeddelelsens kode, sværhedsgrad og tilstand.

En lagret procedure kaldet "sys.sp_addmessage" bruges til at tilføje brugerdefinerede fejlmeddelelser. Vi kan bruge den til at kalde fejlmeddelelsen flere steder.

Vi kan ringe til RAISERROR og sende beskednummeret som en parameter i stedet for at fastkode de samme fejldetaljer flere steder i koden.

Ved at udføre den valgte kode nedefra tilføjer vi den tilpassede fejl i SQL Server, hæver den og bruger derefter sys.sp_dropmessage for at slette den angivne brugerdefinerede fejlmeddelelse:

exec sys.sp_addmessage @msgnum=55000, @severity = 11, 
                                          @msgtext = 'My custom error message'
GO

RAISERROR(55000,11,1)
GO

exec sys.sp_dropmessage @msgnum=55000
GO

Vi kan også se alle meddelelser i SQL Server ved at udføre forespørgselsformularen nedenfor. Vores brugerdefinerede fejlmeddelelse er synlig som det første element i resultatsættet:

SELECT * FROM master.dbo.sysmessages

Opret et system til at logge fejl

Det er altid nyttigt at logge fejl til senere fejlfinding og behandling. Du kan også sætte udløsere på disse loggede tabeller og endda oprette en e-mail-konto og være en smule kreativ i måden at underrette folk, når der opstår en fejl.

For at logge fejl opretter vi en tabel kaldet DBError_Log , som kan bruges til at gemme logdetaljerne:

CREATE TABLE DBError_Log
(
    DBError_Log_ID    INT IDENTITY(1, 1) PRIMARY KEY,
    UserName              VARCHAR(100),
    ErrorNumber    INT,
    ErrorState     INT,
    ErrorSeverity  INT,
    ErrorLine      INT,
    ErrorProcedure VARCHAR(MAX),
    ErrorMessage   VARCHAR(MAX),
    ErrorDateTime  DATETIME
);

For at simulere logningsmekanismen opretter vi GenError lagret procedure, der genererer Division med nul fejl og logger fejlen til DBError_Log tabel:

CREATE PROCEDURE dbo.GenError
AS
  BEGIN TRY
    SELECT 1/0
  END TRY
  BEGIN CATCH
    INSERT INTO dbo.DBError_Log
    VALUES
    (SUSER_SNAME(),
     ERROR_NUMBER(),
     ERROR_STATE(),
     ERROR_SEVERITY(),
     ERROR_LINE(),
     ERROR_PROCEDURE(),
     ERROR_MESSAGE(),
     GETDATE()
	);
  END CATCH
GO

EXEC dbo.GenError
SELECT * FROM  dbo.DBError_Log

DBError_Log tabel indeholder alle de oplysninger, vi har brug for for at fejlfinde fejlen. Det giver også yderligere oplysninger om den procedure, der forårsagede fejlen. Selvom dette kan virke som et trivielt eksempel, kan du udvide denne tabel med yderligere felter eller bruge den til at udfylde den med specialfremstillede undtagelser.

Konklusion

Hvis vi vil vedligeholde og fejlsøge applikationer, vil vi i det mindste rapportere, at noget gik galt, og også logge det under motorhjelmen. Når vi har en applikation på produktionsniveau, der bruges af millioner af brugere, er ensartet og rapporterbar fejlhåndtering nøglen til fejlretningsproblemer i runtime.

Selvom vi kunne logge den oprindelige fejl til databasefejlloggen, skulle brugerne se en mere venlig besked. Det ville derfor være en god idé at implementere brugerdefinerede fejlmeddelelser, der sendes til opkaldsapplikationer.

Uanset hvilket design du implementerer, skal du logge og håndtere bruger- og systemundtagelser. Denne opgave er ikke svær med SQL Server, men du skal planlægge den fra begyndelsen.

Tilføjelse af fejlhåndteringsoperationer på databaser, der allerede kører i produktionen, kan indebære alvorlig koderefaktorering og svære at finde ydeevneproblemer.


  1. Opret dynamisk PHP-objekt baseret på streng

  2. Cloud Vendor Deep-Dive:PostgreSQL på DigitalOcean

  3. Sådan læser og nulstiller du AUTO_INCREMENT i MySQL

  4. (Dansk) Sådan bruger du Oracle Database 19c Pre-Built Developer VM