Try…Catch

Έχουν δημοσιευτεί 03 Δεκεμβρίου 05 11:11 πμ | KelMan 

Επιτέλους! Το error handling πάντα ήταν ένα από τα αδύνατα σημεία της T-SQL. Πλέον στη νέα έκδοση του SQL Server υποστηρίζεται η δομή Try…Catch! Ας δούμε ένα παράδειγμα:

SET tempdb
GO
 
CREATE TABLE DemoTable (ColumnA int PRIMARY KEY, ColumnB int)
CREATE TABLE LogTable (ColumnA int, ColumnB int, error int,  date datetime default GETDATE())
GO
 
CREATE PROCEDURE DoSomething @a int, @b int AS
SET XACT_ABORT ON
BEGIN TRY
 BEGIN TRAN
    INSERT INTO DemoTable VALUES (@a, @b)
 COMMIT TRAN
END TRY
BEGIN CATCH 
  DECLARE @err int
  SET @err = @@error
 ROLLBACK TRAN
  INSERT INTO LogTable VALUES(@a, @b, @err, default)
END CATCH
GO

EXEC DoSomething 1,1
EXEC DoSomething 2,2
EXEC DoSomething 3,3
EXEC DoSomething 1,1

SELECT * FROM LogTable

Μέσα στην procedure, θα παρατηρήσετε ότι η δομή Try…Catch σπάει δε δύο τμήματα. Το πρώτο είναι το BEGIN TRY…END TRY block και το δεύτερο είναι το BEGIN CATCH…END CATCH block. Εντούτοις, αυτά τα δύο αποτελούν μία οντότητα και δεν μπορούν να διαχωριστούν (πχ το πρώτο να είναι σε μια stored procedure και το δεύτερο σε ένα trigger). Ωστόσο, υπάρχει η δυνατότητα για nested Try…Catch structures οπότε αυτό από μόνο του αποτελεί τον πρώτο και καλύτερο λόγο για να τα χρησιμοποιήσει κάποιος στον κώδικά του.

Ένα σημείο που θέλει προσοχή είναι το ποια λάθη πιάνει το Catch block. Υπάρχουν διάφορα λάθη που δεν περνάνε στο Catch block:
Συντακτικά λάθη
Λάθη από recompilation (π.χ. έχουμε κάνει drop έναν πίνακα που υπήρχε όταν φτιάξαμε το stored procedure με το Try…Catch)
Όσα προκαλούν την διακοπή του connection
Όσα έχουν severity level έως και 10

Για τα δύο πρώτα, μπορούμε να καταφύγουμε στο κόλπο του σπασίματος του κώδικα σε πολλαπλές procedures. Ειδικά για τα τελευταία, αν θέλουμε οπωσδήποτε να περνάει ο έλεγχος στο Catch block όταν συμβούν, τότε έχουμε δύο επιλογές. Μπορούμε να ενεργοποιήσουμε το SET XACT_ABORT ON (όπως στο παράδειγμα) ή να ελέγχουμε το global variable @@error μετά από κάθε “επικίνδυνο” statement και κατόπιν να εκτελούμε ένα RAISERROR…WITH TRAN_ABORT ώστε να περάσουμε explicitly τον έλεγχο στο Catch κομμάτι.

Επίσης (αυτό είναι μία από τις παλιότερες αρχές του error handling σε T-SQL), στο Catch block, αν σκοπεύουμε να χρησιμοποιήσουμε το @@error θα πρέπει αμέσως να το αποθηκέψουμε σε μία μεταβλητή γιατί θα χαθεί η τιμή του μετά από οποιοδήποτε statement που θα εκτελεστεί επιτυχώς. Δηλαδή ο παρακάτω κώδικας, αν και τυπικό για περιβάλλον VB.NET, εδώ είναι συνταγή αποτυχίας:

BEGIN CATCH 
   Select 'Unexpected error occurred: ', @@Error
  INSERT INTO LogTable VALUES(@a, @b, @err, default)
END CATCH

Τέλος, καλό θα είναι όταν έχουμε περίπλοκα CATCH blocks να κάνουμε ABORT ΤRAN όσο το δυνατόν συντομότερο για να επελευθερώνουμε resources. Αυτό συμβαίνει γιατί όταν έχει συμβεί κάποιο critical error (ενώ έχουμε κάνει BEGIN TRAN), τότε το transaction μπαίνει σε ένα doomed state από το οποίο δεν μπορεί να βγεί παρά μόνο όταν κάνουμε εμείς ROLLBACK. Μέχρι τότε, μπορούμε να κάνουμε οτιδήποτε αρκεί να μην έχει ως αποτέλεσμα να γραφτεί κάτι στο transaction log.

Δημοσίευση στην κατηγορία:

Σχόλια:

Χωρίς Σχόλια
Έχει απενεργοποιηθεί η προσθήκη σχολίων από ανώνυμα μέλη

Search

Go

Συνδρομές