Postgres'te Geçmiş Veri Sorgulama: 'Geçen Ay Müşteri Verim Nasıldı?' Sorusuna Tek Sorguyla Cevap Verin Siber Güvenlik

Postgres'te Geçmiş Veri Sorgulama: 'Geçen Ay Müşteri Verim Nasıldı?' Sorusuna Tek Sorguyla Cevap Verin

Postgres'te geçmiş veri sorgulama: SCD Type 4 ile tek sorguda eski verilere ulaşın. Trigger ve history tablosu ile zamansal veri sorununu çözün.

Bir gün mutlaka karşınıza çıkar: 'Geçen Şubat ayında kaydolan müşterimizin planı nasıldı?' diye sorarlar. Mevcut satır size bunu söylemez; çünkü o kayıt o zamandan beri üç kez düzenlenmiştir. İşte bu klasik tarihsel/zamansal veri sorunudur: boyutsal verilerdeki değişiklikleri zaman içinde nasıl yönetirsiniz ki 'T anındaki satır' sorgusunu anında cevaplayabilesiniz? Bu makale, bu sorunu tek bir SELECT ile çözen şema desenini anlatıyor.

Zamansal veri modelleme için birkaç yaklaşım vardır. Olay odaklı/event sourcing yaklaşımı, alanın doğal olarak ayrık anlamlı olaylardan oluştuğu durumlar için uygundur (sensör okumaları, reçeteler, stok hareketleri, finansal işlemler). Burada kayıt değişiklikleri olayları doğal durumdur ve veritabanı bu olayları kaydetmek için tasarlanmıştır. Ancak alan kayıtlar (müşteriler, ürünler, sözleşmeler) üzerinden düşünüyorsa ve değişiklikler doğal olay semantiğine sahip değilse kötü bir uyumdur.

Yerinde versiyonlama (SCD Type 2 olarak da bilinir) yaklaşımında veritabanı tablosu, her kaydın tüm versiyonlarını valid_from/valid_to ile birlikte tutar; alan hala kayıtlar üzerinden düşünür. Belirli bir andaki kayıt durumunu almak için valid_from/valid_to penceresinde filtreleme yapmak gerekir. Versiyonların alan anlamı taşıdığı durumlar için harikadır. Ancak uygulama zaten mevcutsa, her mevcut sorgu, görünüm ve join'e WHERE valid_to IS NULL filtresi eklemek gerekir ki bu büyük bir değişiklik gerektirir. Yeşil alan projelerde bu yaklaşımla kardeş history tablosu (SCD Type 4) arasındaki seçim, 'tek tablo + filtre kuralı' ile 'mevcut ve geçmiş ayrı' modellerinden hangisinin takımın zihinsel modeline uyduğuna bağlıdır.

Bir diğer yaklaşım, belirli bir periyotta veya olayda tam bir kopya (snapshot) almaktır. Gecelik snapshot'lar ayrı bir tabloya alınabilir veya bir iş olayı (onay, fatura) gerçekleştiğinde snapshot downstream bir varlığa dondurulabilir. Bu, sorgular belirli anlara sabitlenmişse ve depolama kısıtlıysa verimlidir. Ancak rastgele zamanlar ('Salı günü saat 15:37'de bu nasıldı?') ve sık değişen kayıtlar için snapshot anındaki değerleri alırsınız ve snapshot'lar arasındaki değişikliklerin çoğunu kaçırırsınız.

SCD Type 4 yaklaşımı, ana tablonuza ek olarak bir history tablosu oluşturarak çözüm sunar. Aşağıdaki SQL Postgres içindir; desen, satır düzeyinde trigger destekleyen herhangi bir RDBMS'ye uyarlanabilir. Öncelikle ana tablonuz: CREATE TABLE records (record_id BIGSERIAL PRIMARY KEY, ...); Ardından aynı şekle sahip ancak birkaç ek kitap tutma sütunu içeren bir history tablosu: CREATE TABLE records_history (history_id BIGSERIAL PRIMARY KEY, valid_from TIMESTAMPTZ NOT NULL, valid_to TIMESTAMPTZ, operation TEXT NOT NULL, LIKE records INCLUDING DEFAULTS); LIKE records INCLUDING DEFAULTS, records tablosundaki tüm sütunları kopyalar. Sadece geçerli versiyonun valid_to değeri NULL olur; eski versiyonların valid_to değeri, bir sonraki düzenlemenin onları kapattığı an ile doldurulur.

Her INSERT, UPDATE ve DELETE işlemi records_history tablosuna yansıtılmalıdır. Bir satır düzeyinde trigger, senkronizasyonu sağlar, böylece uygulamanın iki tabloya yazması gerekmez. Trigger fonksiyonu, UPDATE veya DELETE'te eski geçerli versiyonun valid_to değerini now() ile günceller; INSERT veya UPDATE'te yeni bir satır ekler; DELETE'ta ise bir tombstone satırı yazar. Bu yapı ile 'T tarihinde tablo nasıldı?' sorusu tek bir sorgu ile cevaplanabilir: SELECT DISTINCT ON (record_id) * FROM records_history WHERE valid_from <= :as_of AND (valid_to IS NULL OR valid_to > :as_of) AND operation != 'DELETE' ORDER BY record_id, valid_from DESC; Bu sorgu, her kayıt için :as_of anında geçerli olan en son versiyonu döndürür.

Kurulum sırasında dikkat edilmesi gereken bir nokta: trigger eklenmeden önce var olan satırların history girişi olmayacaktır. Bunun için bir defaya mahsus backfill yapılması gerekir: INSERT INTO records_history (valid_from, valid_to, operation, ...) SELECT now(), NULL, 'BOOTSTRAP', r.* FROM records r; Bu sayede her mevcut kaydın BOOTSTRAP versiyonu oluşur. Kurulum öncesi nokta sorguları boş döner (bu dürüsttür, çünkü o zamanlar history yoktu). Kurulum anı ve sonrası sorgulanabilir.

Sonuç ve Değerlendirme

Sonuç olarak, SCD Type 4 deseni, Postgres'te geçmiş veri sorgulama için esnek ve performanslı bir çözüm sunar. Trigger ile otomatikleştirilmiş history tablosu, mevcut uygulama koduna dokunmadan zamansal sorgulara izin verir. Bu deseni kullanarak, 'geçen ay müşteri verim nasıldı?' gibi soruları tek bir SELECT ile cevaplayabilir, veri değişikliklerinin izini kaybetmezsiniz.

Kaynak: dev.to

Paylaş: