Transaction'a Giriş
Transaction, veritabanında yapılan işlemlerin bütünlüğünü korur. İşlemlerin herhangi bir yerinde veritabanı bağlantısı kopması ya da hata oluşması durumunda, tüm yapılan ya da belirtilen noktaya kadar yapılan işlemleri Transaction kullanarak geriye alabiliriz.
Hata denetiminin yanında; doğru veri çekmek için de Transaction kullanılır.
Örnek olarak; yeni bir müşteri kaydı yapıldıktan hemen sonra o müşterinin id’sini veritabanından çekip, ürün sattığımızı düşünelim. Aksilik bu ya tam müşteri kaydını yaptığımız anda, başka bir şubeden başka bir müşteri kaydı araya girdi ve bizim satış için çekeceğimiz max id değeri diğer şubenin müşterisine ait olmuş oldu. Bunun önüne geçmek için tablomuzu yeni veri kaydına kilitlememiz gerekmektedir. Bunun için de transaction yapısını kullanabiliriz.
DECLARE @MusteriId INT
BEGIN TRANSACTION
INSERT INTO Musteriler(Ad,Soyad) VALUES ('Turgay','Sahtiyan')
SET @MusteriId =(SELECT SCOPE_IDENTITY)
INSERT INTO Satislar(MusteriId,UrunKodu,Tutar) VALUES (@MusteriId,'100',50.00)
COMMIT
Tablomuz kilitlendiği için başka bir kayıt araya giremeyecek ve bizim çekeceğimiz max müşteri id değeri, çekmek istediğimiz doğru değer olacaktır.Kilitleme yöntemlerini başka bir makalede ele alacağız.
Dilerseniz hata denetimini bir senaryo örneği ile destekleyerek teorik bilgilerimizi pratiğe dönüştürelim.
Örneğimizde ufak bir bankacılık uygulaması kullanalım.
Veritabanımızda, Kartlar ve KartHareketleri isimli iki basit tablomuz olsun. Her Kart Hareketi işlendiğinde, Kartlar tablosundaki Bakiye alanı güncellensin.
Dilerseniz “Banka” isimli boş bir veritabanı oluşturup aşağıdaki kodu çalıştırarak siz de örneğimize eşlik edebilirsiniz.
USE[Banka]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Kartlar](
[KartNo] [nvarchar](16) NULL,
[Bakiye] [decimal](15, 2) NULL
)ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[KartHareketleri](
[KartNo] [nvarchar](16) NOT NULL,
[Tutar] [decimal](15, 2) NULL
)ON [PRIMARY]
GO
Aşağıdaki komut ile örnekte kullanacağımız kart bilgisini yaratalım. (500 TL Limitli, 12345 numaralı kart)
INSERT INTO Kartlar(KartNo,Bakiye) VALUES ('12345',500.00)
İlk örneğimiz sorunsuz çalışsın.
DECLARE @KartNo NVARCHAR(16) = '12345'
DECLARE @Tutar DECIMAL(15,2) = 200.00
INSERT INTO KartHareketleri(KartNo,Tutar) VALUES (@KartNo,@Tutar)
UPDATE Kartlar SET Bakiye = Bakiye - @Tutar WHERE KartNo = @KartNo

Kodu çalıştırdığımızda Kart Bakiyesi 300 TL’ye düşüyor.
Şimdiki sistemin sanki iki sql cümlesi arasında bağlantı kopmuş gibi davranması için; INSERT cümlesinden sonra veritabanına hata fırlattıran Raise Error komutunu kullanalım.
DECLARE @KartNo NVARCHAR(16) = '12345'
DECLARE @Tutar DECIMAL(15,2) = 50.00
INSERT INTO KartHareketleri(KartNo,Tutar) VALUES (@KartNo,@Tutar)
RAISERROR ('Bağlantı Kayboldu',20, 1) WITH LOG
UPDATE Kartlar SET Bakiye = Bakiye - @Tutar WHERE KartNo = @KartNo
Sadece KartHareketleri tablosuna kayıt oldu. 50 TL’lik işlem gerçekleştiği halde; Kart tablosundaki Bakiye güncellenemedi. İstenmeyen, hatalı bir durum!
Bu istenmeyen durumun önüne geçmek için artık Transaction yapımızı kullanabiliriz.
Transaction yapısını kullanmadan önce kısaca komut setlerine değinirsek;
Transaction “BEGIN TRANSACTION” komutu ile başlatılır. Eğer herşey yolunda giderse “COMMIT” komutu ile sonlandırılır.
Ek olarak kullanılan “SAVE TRANSACTION” ve “ROLLBACK” komutsetleri de vardır. Bunları da birazdan örneklerle açıklamaya çalışacağım.
Az önceki örneğimizi Transaction yapısı içinde kullanalım ve sonuçlarına bakalım.
DECLARE @KartNo NVARCHAR(16) = '12345'
DECLARE @Tutar DECIMAL(15,2) = 50.00
BEGIN TRANSACTION
INSERT INTO KartHareketleri(KartNo,Tutar) VALUES (@KartNo,@Tutar)
RAISERROR ('Bağlantı Kayboldu',20, 1) WITH LOG
UPDATE Kartlar SET Bakiye = Bakiye - @Tutar WHERE KartNo = @KartNo
COMMIT
Commit komutu işlenemeden hata oluştuğu için INSERT işlemi de iptal edildi ve Kart Hareketleri tablosuna herhangi bir kayıt eklenmedi.
Peki sadece bağlantı koptuğunda ya da sistem hataları oluştuğunda mı Transaction kullanılır. Tabii ki hayır.
Örnek olarak Kartımızın bakiyesindeki tutardan daha yüklü bir işlem yapıldığında da işlemlerimizi geri almak isteyebiliriz. Bu şekilde kod tarafında yapılacak hata kontrollerinde işlemlerin geri alınabilmesi için “Rollback” komutu kullanılır. Hemen örneğimizi uygulayalım.
DECLARE @KartNo NVARCHAR(16) = '12345'
DECLARE @Tutar DECIMAL(15,2) = 350.00
DECLARE @Bakiye DECIMAL(15,2)
BEGIN TRANSACTION
INSERT INTO KartHareketleri(KartNo,Tutar) VALUES (@KartNo,@Tutar)
UPDATE Kartlar SET Bakiye = Bakiye - @Tutar WHERE KartNo = @KartNo
SET @Bakiye =(SELECT Bakiye FROM Kartlar WHERE KartNo = @KartNo)
IF (@Bakiye < 0) ROLLBACK
ELSE COMMIT
350 TL’lik bir işlem Bakiyesi 200TL’lik bir karta uygulandığında; Bakiye eksiye düştüğünden işlemler geri alınıyor.
Son olarak da “SavePoint” kavramını inceleyelim.
Bazen yaptığımız işlemde, bir hata oluşması durumunda işlemin toptan iptal edilmesi yerine; belli bir noktaya kadar işlemesini tercih edebiliriz. Bu gibi durumlarda SavePoints (Sabitleme Noktaları) kullanılır.
DECLARE @KartNo NVARCHAR(16)
DECLARE @Tutar DECIMAL(15,2)
DECLARE @Bakiye DECIMAL(15,2)
BEGIN TRANSACTION
--1.islem
SET @KartNo = '12345'
SET @Tutar = '150'
INSERT INTO KartHareketleri(KartNo,Tutar) VALUES (@KartNo,@Tutar)
UPDATE Kartlar SET Bakiye = Bakiye - @Tutar WHERE KartNo = @KartNo
SET @Bakiye =(SELECT Bakiye FROM Kartlar WHERE KartNo = @KartNo)
IF (@Bakiye < 0) ROLLBACK
SAVE TRANSACTION ilk_islem_sonu
--2.islem
SET @KartNo = '12345'
SET @Tutar = '200'
INSERT INTO KartHareketleri(KartNo,Tutar) VALUES (@KartNo,@Tutar)
UPDATE Kartlar SET Bakiye = Bakiye - @Tutar WHERE KartNo = @KartNo
SET @Bakiye =(SELECT Bakiye FROM Kartlar WHERE KartNo = @KartNo)
IF (@Bakiye < 0) ROLLBACK ilk_islem_sonu
ELSE COMMIT
Burada iki tane işlem yapılıyor ve ilk işlem sonuna bir sabitleme noktası konuluyor. Eğer ikinci işlemde bir hata oluşur ise ilk işlem veritabanına kaydediliyor fakat ikinci işleme ait kayıtlar iptal ediliyor.
Transaction’a böylelikle bir giriş yapmış olduk. Bir sonraki makalede kilitleme yöntemlerini örnekleriyle beraber işleyeceğiz.