SQL Server'da Veritabanları Arası Veri Güncelleme: Cross-Database UPDATE Rehberi

Master-Slave ERP kurulumlarında şubeler arası veri nasıl senkronize edilir? SQL Server'da Cross-Database UPDATE, Batch Processing ve Linked Server...

İş Problemi: “Merkezde Değişen Ünvan Adana’ya Neden Gitmedi?”

Özellikle inşaat, perakende veya grup şirket yapıları kullanan büyük çaplı Mikro ERP kurguları genellikle Tek Veritabanı (Single DB) yerine “Multi-Tenant / Multi-Database” tercih eder. (Örn: MikroDB_Holding, MikroDB_A_Sirketi, MikroDB_B_Sirketi vb.) Ancak bu mimarinin karanlık bir yüzü vardır: Veri Uyumsuzluğu.

Muhasebe veya satınalma departmanı Master (Ana) şirkette bir Cari Ünvanı’nı (cari_unvan1) değiştirir, Vergi Dairesi bilgisini günceller. Fakat bu veri alt şubeye otomatik olarak gitmez. Bunun sonucunda, Adana şubesi fatura kesmeye çalıştığında, e-Fatura entegratöründe VKN/Ünvan uymaz ve e-Fatura iptal veya red yer. Şirket itibar zedeleyici bir sürece girer.

Neden Geleneksel Çözümler Batırır? Kullanıcılar genellikle DB üzerine bir TRIGGER yazar. “Şu veritabanında UPDATE oldu mu diğerine de at” sistemi, SQL sunucusunun IO (Input/Output) performansını tüketir ve bir şube DB’sinde kilitlenme (Deadlock) yaşandığında ana üretim hattını da kilitler!

Çözüm: Asenkron Toplu (Batch) Senkronizasyon Algoritması

Bizim AstaFlow tarzı Multi-DB yapılarında tavsiye ettiğimiz olay, “Transaction Riskini Minimize Eden Programlanmış Çapraz Güncelleme” mimarisidir. Bunu SQL Server Agent içerisine konulan periyodik bir “Sync Job” (Örn: Her 15 dakikada bir) ile yaparsanız sıfır hata ve kesintisizlik elde edersiniz.

Aşağıda veritabanını felç etmeden (Batch bloklarıyla) güncelleme yapan bir Kurumsal “Cross-DB Sync” yordamını görebilirsiniz:

-- GÜVENLİ CROSS-DB SENKRONİZASYON STORED YORDAMI
SET NOCOUNT ON;

DECLARE @KaynakDB NVARCHAR(128) = 'MikroDB_Merkez';
DECLARE @HedefDB NVARCHAR(128) = 'MikroDB_Adana';

-- Hangi tablonun hangi sütunları denetlenecek?
-- (Örnekte Müşteri Sicil Kartları-CARI_HESAPLAR Master alınıyor)

DECLARE @SQL_SENKRON NVARCHAR(MAX) = N'
-- TRANSACT-SQL ERROR HANDLING AÇILIYOR
BEGIN TRY
    BEGIN TRANSACTION;
    
    -- ADIM 1: Sadece "Farklı" olan kayıtları hedefleyip performansı artırıyoruz
    -- Hedef tablodaki Ünvan veya VKN, Ana tablodaki ile aynı DEĞİLSE tetiklenir.
    UPDATE Hedef
    SET 
        Hedef.cari_unvan1 = Kaynak.cari_unvan1,
        Hedef.cari_vdaire_no = Kaynak.cari_vdaire_no
    FROM [' + @HedefDB + N'].dbo.CARI_HESAPLAR Hedef
    INNER JOIN [' + @KaynakDB + N'].dbo.CARI_HESAPLAR Kaynak 
        ON Hedef.cari_kod = Kaynak.cari_kod
    WHERE 
        ISNULL(Hedef.cari_unvan1, '''') <> ISNULL(Kaynak.cari_unvan1, '''')
        OR 
        ISNULL(Hedef.cari_vdaire_no, '''') <> ISNULL(Kaynak.cari_vdaire_no, '''');
        
    -- Eğer etkilenen satır yoksa işlem şeffafça devam eder.
    DECLARE @RowAffected INT = @@ROWCOUNT;

    COMMIT TRANSACTION;
    PRINT ''✅ Senkronizasyon BAŞARILI. Güncellenen Kayıt: '' + CAST(@RowAffected AS NVARCHAR);
    
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
        
    -- Hatayı Log tablonuza yazın veya SysAdmin''e alert çıkartın
    PRINT ''❌ KRİTİK SYNC HATASI'';
    PRINT ''Error Message: '' + ERROR_MESSAGE();
END CATCH
';

EXEC sp_executesql @SQL_SENKRON;

Performans / Ölçekleme (Lock ve Timeout Sorunsalı)

Eğer Cari Hesaplar tablonuzda 300.000 kayıt varsa ve siz yukarıdaki gibi salt UPDATE atarsanız, Database Engine tüm hedef tabloya Exclusive Lock (X) uygular. Başka bir memur o saniyede Adana şubesine fatura kaydedemez, SQL Timeout yer.

Nasıl Çözülür (Batch Update Senaryosu): Yüz binlerce satırlık farklarda, 5000 satırlık bloklara ayırıp WAITFOR DELAY (nefes alma) kurgusu eklenerek tablo lock baypas edilebilir:

-- DÖNGÜSEL BATCH UPDATE (Özet Mantık)
WHILE (1=1)
BEGIN
   UPDATE TOP (5000) Hedef SET Hedef.Unvan = Kaynak.Unvan ...
   IF @@ROWCOUNT = 0 BREAK;
   WAITFOR DELAY '00:00:01'; -- 1 Saniye Sunucunun nefes almasına izin ver!
END

Edge Cases (İstisnai Durumlar) ve Mimari Tehlikeler

  1. Farklı Sunucularda Olma Durumu (Linked Servers): Eğer Merkez İstanbul’da ayrı bir Cloud makinesinde, Adana şubesi kendi binasındaki bir sunucuda ise [MikroDB_Merkez].dbo.Cari şeklinde direkt bağlanılamaz! SQL Server’da SP_ADDLINKEDSERVER ile ağ geçidi tanımlanmalıdır. Ancak Linked Server üzerinden Cross-DB UPDATE yapmak ağ gecikmesi (Latency) nedeniyle son derece risklidir; işlem koptuğunda veri yarı yolda kalabilir. Bunun yerine “Veri Aktarımını Yapan API” (Microservice) tercih edilmelidir.
  2. Kayıt “Yoksa” (INSERT) Olasılığı: Yukardaki senaryo INNER JOIN tabanlı bir Edit (UPDATE) mantığıdır. Merkezde açılan yeni bayi henüz şubede hiç açılmadıysa? O zaman MERGE INTO SQL komutuyla “Bulursan Update et, Bulamazsan Arkasına Insert at” mantığı devreye alınmalıdır.
  3. Collation (Dil Karakter Seti) Sorunları: Eğer Merkez şube veritabanı Turkish_CI_AS ile açılmış, Şube DB Latin1_General_CI_AS ile kurulmuşsa (kurulum hatası), Cross-DB Join attığınız esnada “Cannot resolve collation conflict” SQL patlaması yaşarsınız. Join bloklarına COLLATE DATABASE_DEFAULT eklemeniz hayat kurtarır.

Bu Bilgiyi Nereden Biliyoruz? (Kaynaklar)

📚 İlgili Yazılar