Pengantar
Tidak peduli seberapa keras kita mencoba merancang dan mengembangkan aplikasi, kesalahan akan selalu terjadi. Ada dua kategori umum – sintaks atau kesalahan logis dapat berupa kesalahan program atau konsekuensi dari desain database yang salah. Jika tidak, Anda mungkin mendapatkan kesalahan karena input pengguna yang salah.
T-SQL (bahasa pemrograman SQL Server) memungkinkan penanganan kedua jenis kesalahan. Anda dapat men-debug aplikasi dan memutuskan apa yang perlu Anda lakukan untuk menghindari bug di masa mendatang.
Sebagian besar aplikasi mengharuskan Anda mencatat kesalahan, menerapkan pelaporan kesalahan yang mudah digunakan, dan, jika memungkinkan, menangani kesalahan dan melanjutkan eksekusi aplikasi.
Pengguna menangani kesalahan pada tingkat pernyataan. Ini berarti ketika Anda menjalankan sekumpulan perintah SQL, dan masalah terjadi di pernyataan terakhir, semua yang mendahului masalah itu akan dikomit ke database sebagai transaksi implisit. Ini mungkin bukan yang Anda inginkan.
Database relasional dioptimalkan untuk eksekusi pernyataan batch. Dengan demikian, Anda perlu menjalankan sekumpulan pernyataan sebagai satu unit dan gagalkan semua pernyataan jika satu pernyataan gagal. Anda dapat melakukannya dengan menggunakan transaksi. Artikel ini akan fokus pada penanganan kesalahan dan transaksi, karena topik ini sangat terkait.
Penanganan kesalahan SQL
Untuk mensimulasikan pengecualian, kita perlu memproduksinya dengan cara yang dapat diulang. Mari kita mulai dengan contoh paling sederhana – pembagian dengan nol:
SELECT 1/0
Keluaran menjelaskan kesalahan yang dilontarkan – Ditemukan kesalahan bagi dengan nol . Namun kesalahan ini tidak ditangani, dicatat, atau disesuaikan untuk menghasilkan pesan yang mudah digunakan.
Penanganan pengecualian dimulai dengan meletakkan pernyataan yang ingin Anda jalankan di blok BEGIN TRY…END TRY.
SQL Server menangani (menangkap) kesalahan di blok BEGIN CATCH…END CATCH, tempat Anda dapat memasukkan logika khusus untuk pencatatan atau pemrosesan kesalahan.
Pernyataan BEGIN CATCH harus segera diikuti setelah pernyataan END TRY. Eksekusi kemudian diteruskan dari blok TRY ke blok CATCH pada kejadian kesalahan pertama.
Di sini Anda dapat memutuskan cara menangani kesalahan, apakah Anda ingin mencatat data tentang pengecualian yang muncul atau membuat pesan yang mudah digunakan.
SQL Server memiliki fungsi bawaan yang dapat membantu Anda mengekstrak detail kesalahan:
- ERROR_NUMBER():Mengembalikan jumlah kesalahan SQL.
- ERROR_SEVERITY():Mengembalikan tingkat keparahan yang menunjukkan jenis masalah yang dihadapi dan tingkatnya. Level 11 hingga 16 dapat ditangani oleh pengguna.
- ERROR_STATE():Mengembalikan nomor status kesalahan dan memberikan detail lebih lanjut tentang pengecualian yang dilemparkan. Anda menggunakan nomor kesalahan untuk mencari di basis pengetahuan Microsoft untuk detail kesalahan tertentu.
- ERROR_PROCEDURE():Mengembalikan nama prosedur atau pemicu yang memunculkan kesalahan, atau NULL jika kesalahan tidak terjadi dalam prosedur atau pemicu.
- ERROR_LINE():Mengembalikan nomor baris tempat kesalahan terjadi. Ini bisa berupa nomor baris prosedur atau pemicu atau nomor baris dalam batch.
- ERROR_MESSAGE():Mengembalikan teks pesan kesalahan.
Contoh berikut menunjukkan cara menangani kesalahan. Contoh pertama berisi Pembagian dengan nol kesalahan, sedangkan pernyataan kedua benar.
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
Jika pernyataan kedua dijalankan tanpa penanganan kesalahan (PILIH 'Teks yang benar'), itu akan berhasil.
Karena kami menerapkan penanganan kesalahan khusus di blok TRY-CATCH, eksekusi program diteruskan ke blok CATCH setelah kesalahan dalam pernyataan pertama, dan pernyataan kedua tidak pernah dieksekusi.
Dengan cara ini, Anda dapat memodifikasi teks yang diberikan kepada pengguna dan mengontrol apa yang terjadi jika kesalahan terjadi dengan lebih baik. Misalnya, kami mencatat kesalahan ke tabel log untuk analisis lebih lanjut.
Menggunakan transaksi
Logika bisnis mungkin menentukan bahwa penyisipan pernyataan pertama gagal saat pernyataan kedua gagal, atau bahwa Anda mungkin perlu mengulangi perubahan pernyataan pertama pada kegagalan pernyataan kedua. Menggunakan transaksi memungkinkan Anda mengeksekusi sekumpulan pernyataan sebagai satu unit yang gagal atau berhasil.
Contoh berikut menunjukkan penggunaan transaksi.
Pertama, kami membuat tabel untuk menguji data yang disimpan. Kemudian kami menggunakan dua transaksi di dalam blok TRY-CATCH untuk mensimulasikan hal-hal yang terjadi jika bagian dari transaksi gagal.
Kami akan menggunakan pernyataan CATCH dengan pernyataan XACT_STATE(). Fungsi XACT_STATE() digunakan untuk memeriksa apakah transaksi masih ada. Jika transaksi dibatalkan secara otomatis, TRANSAKSI ROLLBACK akan menghasilkan pengecualian baru.
Dapatkan looting pada kode di bawah ini:
-- 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
Gambar menunjukkan nilai dalam tabel TEST_TRAN dan pesan kesalahan:
Seperti yang Anda lihat, hanya nilai pertama yang dilakukan. Pada transaksi kedua, kami memiliki kesalahan konversi jenis di baris kedua. Dengan demikian, seluruh batch dibatalkan.
Dengan cara ini, Anda dapat mengontrol data apa yang masuk ke database dan bagaimana batch diproses.
Membuat pesan kesalahan khusus dalam SQL
Terkadang, kami ingin membuat pesan kesalahan khusus. Biasanya, mereka dimaksudkan untuk skenario ketika kita tahu bahwa masalah mungkin terjadi. Kami dapat membuat pesan kustom kami sendiri yang mengatakan bahwa sesuatu yang salah telah terjadi tanpa menunjukkan detail teknis. Untuk itu, kami menggunakan kata kunci 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
Atau, kami ingin memiliki katalog pesan kesalahan khusus untuk kategorisasi dan konsistensi pemantauan dan pelaporan kesalahan. SQL Server memungkinkan kita untuk menentukan kode pesan kesalahan, tingkat keparahan, dan status.
Prosedur tersimpan yang disebut "sys.sp_addmessage" digunakan untuk menambahkan pesan kesalahan khusus. Kita dapat menggunakannya untuk memanggil pesan kesalahan di banyak tempat.
Kami dapat memanggil RAISERROR dan mengirim nomor pesan sebagai parameter alih-alih mengkodekan detail kesalahan yang sama di beberapa tempat dalam kode.
Dengan mengeksekusi kode yang dipilih dari bawah, kami menambahkan kesalahan khusus ke SQL Server, meningkatkannya, dan kemudian menggunakan sys.sp_dropmessage untuk menghapus pesan kesalahan yang ditentukan pengguna yang ditentukan:
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
Juga, kita dapat melihat semua pesan di SQL Server dengan menjalankan formulir kueri di bawah ini. Pesan kesalahan khusus kami terlihat sebagai item pertama di hasil:
SELECT * FROM master.dbo.sysmessages
Buat sistem untuk mencatat kesalahan
Itu selalu berguna untuk mencatat kesalahan untuk debugging dan pemrosesan nanti. Anda juga dapat menempatkan pemicu pada tabel yang dicatat ini dan bahkan menyiapkan akun email dan sedikit kreatif dalam cara memberi tahu orang-orang ketika terjadi kesalahan.
Untuk mencatat kesalahan, kami membuat tabel bernama DBError_Log , yang dapat digunakan untuk menyimpan data detail log:
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
);
Untuk mensimulasikan mekanisme logging, kami membuat GenError prosedur tersimpan yang menghasilkan Pembagian dengan nol kesalahan dan mencatat kesalahan ke 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 berisi semua informasi yang kita butuhkan untuk men-debug kesalahan. Juga, ini memberikan informasi tambahan tentang prosedur yang menyebabkan kesalahan. Meskipun ini mungkin tampak seperti contoh sepele, Anda dapat memperluas tabel ini dengan bidang tambahan atau menggunakannya untuk mengisinya dengan pengecualian yang dibuat khusus.
Kesimpulan
Jika kami ingin memelihara dan men-debug aplikasi, kami setidaknya ingin melaporkan bahwa ada yang tidak beres dan juga mencatatnya di bawah tenda. Saat kami memiliki aplikasi tingkat produksi yang digunakan oleh jutaan pengguna, penanganan kesalahan yang konsisten dan dapat dilaporkan adalah kunci untuk men-debug masalah dalam waktu proses.
Meskipun kami dapat mencatat kesalahan asli ke log kesalahan basis data, pengguna akan melihat pesan yang lebih ramah. Oleh karena itu, sebaiknya terapkan pesan kesalahan khusus yang ditampilkan ke aplikasi pemanggil.
Apa pun desain yang Anda terapkan, Anda perlu mencatat dan menangani pengecualian pengguna dan sistem. Tugas ini tidak sulit dengan SQL Server, tetapi Anda perlu merencanakannya dari awal.
Menambahkan operasi penanganan kesalahan pada database yang sudah berjalan dalam produksi mungkin melibatkan pemfaktoran ulang kode yang serius dan masalah kinerja yang sulit ditemukan.