Transaction ve Database Engine – Bölüm 54
Bu bölümde, batch’ler ile SQL Server üzerinde çalıştırılan kodun davranışını kontrol etmemizi sağlayan transaction yapısını karşılaştıracağız. Transaction çalışırken runtime hatası oluştuğunda işlemin geri alınıp alınmaması gibi yapılması gereken işlemlerin yönetimi sağlanabilmektedir.
Transaction Yapısı
Önceki bölümlerde, SQL Server’da çalıştırılan bir batch’in çözümleme, optimizasyon ve yürütme birimleri olarak gönderilen bir T-SQL ifade yığını olduğunu öğrenmiştik. Bir batch, transaction yardımıyla veri tabanı motoruna gönderilen bir birimden veri tabanı motoru tarafından gerçekleştirilen bir iş birimine genişletilir. Bir transaction, SQL Server tarafından ya hep ya hiç mantığıyla çalıştırılan bir T-SQL ifade dizisidir.
Transaction’lar genellikle iki şekilde oluşturulmaktadır:
Autocommit transaction’lar: Diğer komutlar aracılığıyla başka yolla gönderilen bağımsız veri değiştirme ifadeleri (örneğin, INSERT, UPDATE ve DELETE) SQL Server tarafından bir transaction işleminde otomatik olarak kapsüllenir. Bu transaction’lar, ifade başarılı olduğunda otomatik olarak commit edilir ya da runtime hatasıyla karşılaşılırsa işlem rollback edilir
Explicit transaction’lar: Kullanıcı tarafından başlatılan transaction’ların oluşturulması, gönderilen koda dayalı olarak transaction’ın başlatılma işlemi, commit ve rollback gibi işlemleri gerçekleştiren TCL komutları ile yapılmaktadır. Bu arada TCL, T-SQL’in bir alt kümesidir.
Transaction yapısının en temel özelliği, transaction’ın sınırları içerisindeki bütün faaliyetlerin bütünsel olarak nihayette başarılı veya başarısız olması gerektiğidir. Faaliyetlerinin bir kısmının tamamlanıp diğer kısmının çalıştırılmaması diye bir şey yoktur. Kullanıcılar transaction’ları genellikle mantıksal olarak birlikte gerçekleşmesi gereken işlemler için kullanırlar. Örneğin bir ticari faaliyet sonucu tabloya kayıt işlenmesi gibi.
Aşağıdaki sorguda, bir sipariş işlemi sonucu iki ayrı ifadeyle iki tabloya da veri eklenmiştir:
INSERT INTO dbo.SimpleOrders(custid, empid, orderdate)
VALUES (68,9,'2006-07-12');
INSERT INTO dbo.SimpleOrderDetails(orderid,productid,unitprice,qty)
VALUES (1, 2,15.20,20);
GO
İş kuralları gereği bir sipariş işlemi, sipariş ayrıntılarının yalnızca her iki tabloya da başarıyla eklendiyse tamamlanmış olarak nitelendirilir. Bir sonraki bölümde anlatacağımız gibi, batch’de oluşan bir runtime hatası verilerin bir tabloya başarıyla eklenirken diğerine eklenememesine neden olabilir. Diğer tarafta kullanıcı tanımlı transaction içindeki her iki INSERT ifadesi de başarısız olursa ilk tabloya işlenen veriyi geri alma imkanı vardır. Fakat bir batch ile bu işlem mümkün değildir.
SQL Server, kaynaklar etkin olduğunda transaction’lar için kaynak yönetimini kendisi sağlar. SQL Server gerektiğinde transaction tarafından yapılan değişikliklerin rollback edilmesi amacıyla transaction log içerisine kaynaklar aracılığıyla lock ve entry’ler sağlayabilir.
Batch Sorunları ve Transaction
T-SQL ifadeleri batch olarak sunucuya gönderilirken, yapılandırılmış exception handling işlemi TRY/CATCH bloklarında uygulansa bile runtime hatası olduğunda işin yarım kalan kısmıyla alakalı hiçbir şey yapılamamaktadır. Aşağıdaki örnekte bu sorun örneklendirilmiştir:
BEGIN TRY
INSERT INTO dbo.SimpleOrders(custid,
empid, orderdate)
VALUES (68,9,'2006-07-12');
INSERT INTO dbo.SimpleOrders(custid, empid, orderdate)
VALUES (88,3,'2006-07-15');
INSERT INTO dbo.SimpleOrderDetails(orderid,productid,unitprice,qty)
VALUES (1, 2,15.20,20);
INSERT INTO dbo.SimpleOrderDetails(orderid,productid,unitprice,qty)
VALUES (999,77,26.20,15);
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrNum, ERROR_MESSAGE() AS ErrMsg;
END CATCH;
Bir hata sonucu ilk INSERT işlemi başarılı olur da sonraki insert başarısız olursa, hata CATCH ile yakalansa bile dbo.SimpleOrders tablosundaki yeni kayıt, batch bittiğinde tabloda tutulur, geri alınamaz. Bu sorun, bir ifade çalıştırılmasının ardından bir sonraki ifade çalıştırıldığında runtime hatası oluşan ifadeler için geçerlidir.
Not: Syntax veya ad çözümlemesi, tüm batch’in hata basmasına neden olarak batch’in çalıştırılmasını önler.
SQL Server’da bu sorun batch’i transaction olarak tanımlayarak geçici olarak çözebiliriz.
Transaction Batch’ten Türer
Görüldüğü üzere gibi, batch yürütülürken karşılaşılan runtime hataları, beklenmeyen kısmi bir başarı oluşturabilir. Bunu yönetebilmek için batch’i BEGIN TRANSACTION ve COMMIT TRANSACTION ifadeleri arasına yerleştirerek batch’i bir transaction olarak tanımlayabiliriz. Hata oluştuğunda yapılan rollback işlemi için exception handling kodu tanımlanmaktadır. Bu kodla, hata oluşmadan önce yapılan değişikliklerin hepsi geri alınır.
Aşağıdaki örnekte, hata oluşma olasılığını göstermek için TCL komutları kullanılmıştır:
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO dbo.SimpleOrders(custid, empid, orderdate)
VALUES (68,9,'2006-07-15');
INSERT INTO dbo.SimpleOrderDetails(orderid,productid,unitprice,qty)
VALUES (99, 2,15.20,20);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrNum, ERROR_MESSAGE() AS ErrMsg;
ROLLBACK TRANSACTION;
END CATCH;
Buradaki INSERT ifadeleri, BEGIN TRANSACTION ve COMMIT TRANSACTION ifadeleri arasına alınarak INSERT ifadeleri tek bir birim olarak işlenmektedir. Runtime hatası oluşmazsa transaction ve INSERT işlemleri veri tabanına işlenecektir.
İlk INSERT ifadesinin yürütülmesi sırasında bir hata oluşursa, ikinci INSERT ifadesi atlanarak CATCH bloguna geçilir. CATCH ifadesindeki ROLLBACK ile kaynaklar serbest bırakılarak işlem sonlandırılır.
İlk insert başarılı çalıştıktan sonra ikinci INSERT ifadesinin yürütülmesi sırasında bir hata oluşursa da, yürütme yine CATCH bloğuna geçecektir. İlk INSERT başarıyla tamamlandığından ve dbo.SimpleOrders tablosuna satır eklenmiş olur. Bu başarılı olan INSERT işlemini de geri almak için ROLLBACK ifadesi kullanılmıştır.
Bir sonraki bölümde BEGIN TRANSACTION, COMMIT TRANSACTION ve ROLLBACK TRANSACTION ifadelerini anlatacağız.
Sonraki yazıda transaction adımlarından bahsedeceğiz.