Dalam SQL, transaksi digunakan untuk menjaga integritas data dengan memastikan bahwa urutan pernyataan SQL dijalankan sepenuhnya atau tidak sama sekali.
Transaksi mengelola urutan pernyataan SQL yang harus dijalankan sebagai satu unit kerja, sehingga database tidak pernah berisi hasil operasi parsial.
Saat transaksi membuat beberapa perubahan ke database, semua perubahan berhasil saat transaksi dilakukan, atau semua perubahan dibatalkan saat transaksi dibatalkan.
Kapan Menggunakan Transaksi?
Transaksi sangat penting dalam situasi di mana integritas data akan berisiko jika salah satu dari urutan pernyataan SQL gagal.
Misalnya, jika Anda memindahkan uang dari satu rekening bank ke rekening bank lain, Anda perlu mengurangi uang dari satu rekening, dan menambahkannya ke rekening lainnya. Anda tidak ingin itu gagal di tengah jalan, jika tidak, uang dapat didebit dari satu akun tetapi tidak dikreditkan ke akun lainnya.
Kemungkinan alasan kegagalan dapat mencakup dana yang tidak mencukupi, nomor rekening tidak valid, kegagalan perangkat keras, dll.
Jadi Anda tidak ingin berada dalam situasi di mana tetap seperti ini:
Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
Itu akan menjadi benar-benar buruk. Basis data akan memiliki data yang tidak konsisten dan uang akan hilang begitu saja. Kemudian bank akan kehilangan pelanggan (bank mungkin akan kehilangan semua pelanggannya jika ini terus terjadi), dan Anda akan kehilangan pekerjaan.
Untuk menyelamatkan pekerjaan Anda, Anda dapat menggunakan transaksi seperti ini:
START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION
Anda dapat menulis logika kondisional di dalam transaksi yang akan mengembalikan transaksi jika terjadi kesalahan.
Misalnya, jika terjadi kesalahan antara mendebet akun 1 dan mengkredit akun 2, seluruh transaksi dibatalkan.
Oleh karena itu, hanya ada dua kemungkinan hasil:
Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
Atau:
Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)
Ini adalah penggambaran yang disederhanakan, tetapi ini adalah ilustrasi klasik tentang cara kerja transaksi SQL. Transaksi SQL memiliki ACID.
Jenis Transaksi
Transaksi SQL dapat dijalankan dalam mode berikut.
Mode transaksi | Deskripsi |
---|---|
Transaksi komit otomatis | Setiap pernyataan individual adalah transaksi. |
Transaksi implisit | Transaksi baru secara implisit dimulai ketika transaksi sebelumnya selesai, tetapi setiap transaksi diselesaikan secara eksplisit, biasanya dengan COMMIT atau ROLLBACK pernyataan tergantung pada DBMS. |
Transaksi eksplisit | Secara eksplisit dimulai dengan baris seperti START TRANSACTION , START TRANSACTION atau serupa, tergantung pada DBMS, dan secara eksplisit berkomitmen atau dibatalkan dengan pernyataan yang relevan. |
Transaksi dengan cakupan batch | Hanya berlaku untuk beberapa set hasil aktif (MARS). Transaksi eksplisit atau implisit yang dimulai di bawah sesi MARS menjadi transaksi dengan cakupan batch. |
Mode yang tepat dan pilihan yang tersedia mungkin tergantung pada DBMS. Tabel ini menguraikan mode transaksi yang tersedia di SQL Server.
Dalam artikel ini, kami terutama berfokus pada transaksi eksplisit.
Lihat Bagaimana Transaksi Implisit Bekerja di SQL Server untuk diskusi tentang perbedaan antara transaksi implisit dan autocommit.
Sytnax
Tabel berikut menguraikan sintaks dasar untuk memulai dan mengakhiri transaksi eksplisit di beberapa DBMS yang lebih populer.
DBMS | Sintaks Transaksi Eksplisit |
---|---|
MySQL, MariaDB, PostgreSQL | Transaksi eksplisit dimulai dengan START TRANSACTION atau BEGIN penyataan. COMMIT melakukan transaksi saat ini, membuat perubahannya permanen. ROLLBACK mengembalikan transaksi saat ini, membatalkan perubahannya. |
SQLite | Transaksi eksplisit dimulai dengan START TRANSACTION pernyataan dan diakhiri dengan COMMIT atau ROLLBACK penyataan. Bisa juga diakhiri dengan END pernyataan. |
SQL Server | Transaksi eksplisit dimulai dengan START TRANSACTION pernyataan dan diakhiri dengan COMMIT atau ROLLBACK pernyataan. |
Oracle | Transaksi eksplisit dimulai dengan SET TRANSACTION pernyataan dan diakhiri dengan COMMIT atau ROLLBACK pernyataan. |
Dalam banyak kasus, kata kunci tertentu bersifat opsional saat menggunakan transaksi eksplisit. Misalnya di SQL Server dan SQLite, Anda cukup menggunakan BEGIN
(daripada START TRANSACTION
) dan/atau Anda bisa mengakhirinya dengan COMMIT TRANSACTION
(bukan hanya COMMIT
).
Ada juga berbagai kata kunci dan opsi lain yang dapat Anda tentukan saat membuat transaksi, jadi lihat dokumentasi DBMS Anda untuk sintaks lengkapnya.
Contoh Transaksi SQL
Berikut ini contoh transaksi sederhana di SQL Server:
BEGIN TRANSACTION
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;
Dalam hal ini, informasi pesanan dihapus dari dua tabel. Kedua pernyataan tersebut diperlakukan sebagai satu unit kerja.
Kita bisa menulis logika kondisional ke dalam transaksi kita untuk mengembalikannya jika terjadi kesalahan.
Menamai Transaksi
Beberapa DBMS memungkinkan Anda untuk memberikan nama untuk transaksi Anda. Di SQL Server, Anda dapat menambahkan nama pilihan Anda setelah BEGIN
dan COMMIT
pernyataan.
BEGIN TRANSACTION MyTransaction
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;
Contoh Rollback Transaksi SQL 1
Ini contoh sebelumnya lagi, tetapi dengan beberapa kode tambahan. Kode tambahan digunakan untuk mengembalikan transaksi jika terjadi kesalahan.:
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
pernyataan mengimplementasikan penanganan kesalahan di SQL Server. Anda dapat menyertakan grup pernyataan T-SQL apa pun dalam TRY
memblokir. Kemudian, jika terjadi kesalahan pada TRY
blok, kontrol diteruskan ke kelompok pernyataan lain yang diapit CATCH
blokir.
Dalam hal ini, kami menggunakan CATCH
blokir untuk mengembalikan transaksi. Mengingat itu ada di CATCH
blokir, rollback hanya terjadi jika ada kesalahan.
Contoh Rollback Transaksi SQL 2
Mari kita lihat lebih dekat database yang baru saja kita hapus barisnya.
Pada contoh sebelumnya, kami menghapus baris dari Orders
dan OrderItems
tabel dalam database berikut:
Dalam database ini, setiap kali pelanggan melakukan pemesanan, baris dimasukkan ke dalam Orders
tabel, dan satu atau lebih baris ke dalam OrderItems
meja. Jumlah baris yang dimasukkan ke dalam OrderItems
tergantung pada berapa banyak produk berbeda yang dipesan pelanggan.
Juga, jika itu adalah pelanggan baru, baris baru dimasukkan ke dalam Customers
tabel.
Dalam hal ini, baris perlu dimasukkan ke dalam tiga tabel.
Jika terjadi kegagalan, kami tidak ingin baris dimasukkan ke dalam Orders
tabel tetapi tidak ada baris yang sesuai di OrderItems
meja. Itu akan menghasilkan pesanan tanpa item pesanan. Pada dasarnya, kami ingin kedua tabel diperbarui sepenuhnya atau tidak sama sekali.
Itu sama ketika kami menghapus baris. Kami ingin semua baris dihapus atau tidak ada sama sekali.
Di SQL Server, kita bisa menulis transaksi berikut untuk INSERT
pernyataan.
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
Contoh ini mengasumsikan bahwa ada logika di tempat lain yang menentukan apakah pelanggan sudah ada di database atau tidak.
Pelanggan dapat dimasukkan di luar transaksi ini:
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
Jika transaksi gagal, pelanggan akan tetap berada di database (tetapi tanpa pesanan). Aplikasi perlu memeriksa apakah pelanggan sudah ada sebelum melakukan transaksi.
Transaksi SQL dengan Savepoints
Savepoint mendefinisikan lokasi di mana transaksi dapat kembali jika bagian dari transaksi dibatalkan secara kondisional. Di SQL Server, kami menentukan savepoint dengan SAVE TRANSACTION savepoint_name
(di mana savepoint_name adalah nama yang kita berikan ke savepoint).
Mari kita tulis ulang contoh sebelumnya untuk menyertakan savepoint:
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;
Di sini, kami telah menetapkan savepoint langsung setelah pelanggan INSERT
penyataan. Nanti dalam transaksi, saya menggunakan ROLLBACK
pernyataan untuk menginstruksikan transaksi untuk melakukan rollback ke savepoint tersebut.
Ketika saya menjalankan pernyataan itu, pelanggan dimasukkan, tetapi tidak ada informasi pesanan yang dimasukkan.
Jika transaksi dikembalikan ke savepoint, transaksi harus dilanjutkan hingga selesai dengan lebih banyak pernyataan SQL jika diperlukan dan COMMIT TRANSACTION
pernyataan, atau harus dibatalkan sama sekali dengan memutar kembali seluruh transaksi.
Jika saya memindahkan ROLLBACK
pernyataan kembali ke INSERT
sebelumnya pernyataan, seperti ini:
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;
Ini menghasilkan kesalahan konflik kunci asing. Secara khusus, saya mendapatkan kesalahan berikut:
(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)
Ini terjadi karena, meskipun pesanan telah dimasukkan, operasi itu dibatalkan ketika kami memutar kembali ke savepoint. Kemudian transaksi dilanjutkan sampai selesai. Tetapi ketika menemukan item pesanan terakhir, tidak ada pesanan yang sesuai (karena itu telah dibatalkan), dan kami mendapatkan kesalahan.
Ketika saya memeriksa database, pelanggan dimasukkan, tetapi sekali lagi, tidak ada informasi pesanan yang dimasukkan.
Anda dapat mereferensikan savepoint yang sama dari beberapa tempat dalam transaksi jika diperlukan.
Dalam praktiknya, Anda akan menggunakan pemrograman bersyarat untuk mengembalikan transaksi ke savepont.
Transaksi Bertingkat
Anda juga dapat menyarangkan transaksi di dalam transaksi lain jika diperlukan.
Seperti ini:
BEGIN TRANSACTION Transaction1;
UPDATE table1 ...;
BEGIN TRANSACTION Transaction2;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRANSACTION Transaction2;
UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;
Seperti yang disebutkan, sintaks persis yang Anda gunakan untuk membuat transaksi akan bergantung pada DBMS Anda, jadi periksa dokumentasi DBMS Anda untuk gambaran lengkap opsi Anda saat membuat transaksi di SQL.