"Fact tablosu" niyetine kullanabileceğimiz tablolar kabaca 2 farklı tipte olabilir: Transactional veri içerenler ve snapshot'lar. Peki transactional veri ne demek, snapshot ne demek?
Transactional Veri
Transactional veri, zaman içerisinde gerçekleşen "event" (olay) tarzı kayıtları içeren verilerdir. Örneğin her gün sisteme girdiğiniz siparişler, hesap hareketleri, depo-giriş çıkış hareketleri transactional veriye örnek olarak verilebilir. Tarih ilerledikçe yeni kayıtlar oluşur. SUM tarzı basit aggregation fonksiyonlarıyla bu "event" leri özetlemek ve zamanla eşleştirmek gayet kolaydır. Ayın 1'inde 100, 2'sinde 200, 3'ünde 300 TL'lik sipariş aldınız, gün bazında bunları kolaylıkla gösterebileceğiniz gibi, ayın 3'ü itibariyle basit bir SUM ile 600 TL'lik açık siparişiniz olduğunu kolaylıkla bulursunuz.
Snapshot veriyle farkını daha iyi anlamak açısından "banka hesap hareketleri" örneğini kullanacağım. X bankada hesap açtınız, açılışta 0 bakiyeniz var, ayın 1'inde -diyelim- maaşınız yattı 1000 TL, 2'sinde 300 TL fatura ödediniz, 3'ünde 500 TL para çektiniz. Her bir gün itibariyle hesabınızda ne kadar paranız olduğunu basit bir kümülatif formülüyle rahatlıkla bulursunuz. Bu, transactional veri.
Snapshot Veri
Snapshot veri ise size şunu der: Ayın 3'ü itibariyle hesap bakiyeniz 200 TL!. Ayın 1 ve 2 bilgisi yok, giriş çıkış detaylarını bilmiyoruz, sadece ve sadece ilgili tarih itibariyle geçerli bir bilgi, anlık bir data. Tarihçeye inemiyorsunuz! 5000 vardı da 4800 çekince mi 200 kaldı bilmek mümkün değil! Sadece ancak günlük snapshot veriniz varsa bu bilgiyi -belki- çıkartabilirsiniz, o da her zaman mümkün olmayabilir.
Transactional veri'den snapshot'ı hesaplamak mümkün ama tersi -genellikle- doğru değil!
Bazen mecburiyetten kaynaklanan snapshot tablolar var, -misal- eğitim ya da danışmanlık verdiğim hemen tüm banka ve sigorta müşterilerimde kaçınılmaz olarak snapshot veriler var, "legacy" tabir edilen bazı sistemler var, verisine doğrudan erişmek mümkün değil, ancak belli tarihlerde veriyi "export" edebiliyorsunuz. Örneğin her Pazartesi ya da her ayın ilk günü export edilmiş veriyle çalışabiliyorsunuz. Misal riskli krediler toplamı geçen ayın 1'inde 100 TL'ymiş, bu ayın 1'inde 80 TL olmuş, 20 TL azalmış bilgisini gösterebiliyorsunuz sadece. Ama arada 1 milyon arttı da sonra gene 1 milyon azaldığı için mi 20 oldu bilgisi kayıp!
Bu tür kendini tekrar eden snapshot veriler üzerinden birşeyler çıkartmak mümkün, ama çözüm ilgili snapshot verinin mantığını anlayıp ona göre çözüm üretmekten geçiyor. Standart bir çözüm yok! Hemen hemen tüm snapshot tipi tablolarla ilgili en büyük "konu" ise basit SUM'larla bu işin altından kalkmanın pek mümkün olmaması.
Sıklıkla karşıma çıkan bir örnek olduğu için "eldeki stok" örneğini kullanacağım. Misal örnek Contoso modelindeki stok/satış yapısı şöyle:
Satış verisi transactional veri, dolayısıyla basit bir SUM aldığınızda herşey kolay, ama stok verisi öyle değil, X ürününün Y mağazadaki T tarihindeki stok bilgisini gösteriyor. İlgili tarihe ait "eldeki stok" verisi yani.
Satışlar := SUM( 'Satışlar'[Miktar] ) Stok := SUM( Stok[Stok] )
Bu metrikleri matrise Ürün – Ay – Gün – Magaza kodu bazında düşürdüğümüzde sorun kendiğilinden ortaya çıkıyor! Satış miktarının basit toplamı sorunsuz. Ama stok miktarının toplamı sadece gün seviyesinde ve mağaza stok toplamı seviyesinde anlamlı. İlgili üründen 3 Şubat'ta iki mağazada toplam 22 ürün var. Ama ay seviyesinde ve dip toplamda bu metrik anlamsız! 3 Şubat'ta 22, 10 Şubat'ta 10 ürün stoğu var deyip Şubat seviyesinde 32 stoğumuz var diyemeyiz!
Herhangi bir ürün için ay sonu stoğu, o ürünün en son stoğunun bulunduğu tarihlerdeki mağaza stoklarının toplamıdır gibi görece kompleks bir hesap yapmamız gerekir. Yazının konusu doğrudan DAX olmadığı için uzun uzun anlatmayacağım ama kafamızın çalışma şekline ve DAX tecrübemize göre CALCULATETABLE, LASTDATE veya LASTNONBLANK gibi fonksiyonlarla bunu bulabiliriz.
Doğru Stok := CALCULATE ( SUM ( 'Stok'[Stok] ), CALCULATETABLE ( LASTNONBLANK ( 'Tarih'[Tarih], NOT ( ISEMPTY ( 'Stok' ) ) ), ALL ( 'Mağaza' ) ) )
Tarih tablosundaki her bir tarih için stok var mı tüm mağazalar üstünden kontrol et, stok toplamını bunun üstünden al gibi bir mantık!
Ya da;
Doğru Stok Alternatif := CALCULATE( SUM('Stok'[Stok] ), LASTDATE( CALCULATETABLE( VALUES( 'Stok'[Tarih] ), ALL('Mağaza') ) ) )
Stoğun olduğu en son tarihi tüm mağazalar üzerinden bul, stoğu bunun üstünden hesapla. Gibi!
Gibi diyorum, çünkü yazdığım örnek formüllerin doğru çalışıp çalışmaması snapshot veriyi doğru anlayıp anlamadığımızla ilgili! Stok bilgisinin olmadığı günler "0" stok var demekse -misal- kurduğumuz mantığı değiştirmek gerekir.
Bu durumun özeti şu, her snapshot veri, mantığı neyse ona göre farklı formüller/yaklaşımlar gerektirebilir!
Her ayın ilk günü bakiye hesabını gösteren bir snapshot veriniz varsa, aylık metriği bir önceki ay değerinden çıkartırsınız. Duruma göre metrik yazmamız gerekiyor yani.
Eğer bir "planlamacıysanız" misal, açık sipariş-anlık stok– geçmiş 3 aylık ortalama satış- önümüzdeki 3 ayın tahmini gibi bir sistem üzerinden planlama yapıyorsanız "anlık stok" işinizi çözebilir. Ama yazmanız gereken DAX kodu, transactional veriye göre daha kompleks olacaktır.
Ama şu vereceğim örnek "komple" yanlış yaklaşım: Finansal rapor yapıyorsunuz, snapshot veriyi hesaplayan bir sorgu yazdınız (yazdığınız sorgu da muhtemelen kısa sürede sonuç döndürmüyordur) PowerBI ile de matrisini yaptınız! Ortalama alacak-borç, P&L raporu, artık ne hazırlıyorsanız! Böyle bir rapor, karar vericinin hiçbir işine yaramaz! Ortalama alacak vadesi -diyelim- 40 gün. Geçen ay kaçtı? Bilmiyoruz! Ne aksiyon almamız gerektiği konusunda hiçbir ipucu vermiyor! Bu örneğin "doğru" yaklaşımı ise şu olmalı, transactional hesap hareketlerinizi çekersiniz veri olarak ve tüm modeli / metrikleri bu veri üzerinden DAX ile çözersiniz.
Hazır örneklerini vermişken şunu da demeden geçmeyeyim, örneklerini çok gördüm çünkü: Anlık stok veya ortalama alacak-borç, EBITDA hesabı yapan "şahane"!! T-SQL sorguları gördüm. F5'e basınca sorgunun sonuç döndürmesi yarım saat sürüyor!
"Modelimiz çok geç refresh ediyor" şikayetiyle incelediğim modellerin çoğunun arkasındaki en büyük problemlerden biri buydu: Arkada snapshot hesaplayan bir sorgu var, o sorgunun başka başka alt sorguları var vs.! Sonuç döndürmesi yarım saat olan bir sorguyu veri kaynağı olarak kullanırsanız modelin 3 dakikada refresh etmesini bekleyemezsiniz!
…
Özetle, zorunlu durumlar dışında snapshot veri tipi tercih edilmemesi gereken bir veri tipi. Modellemeyi kolaylaştırmak veya yazdığımız metriklerin kompleksliğini azaltmak için transactional veriden transition matrix diye tabir edilen snapshot tablolar ürettiğimiz olur bazen. Ama tüm modelin ana veri kaynağı olarak snapshot veriyi kullanmak -çoğu zaman- kendi ayağımıza kurşun sıkmak gibi.
Yazıdaki modeli indirebilirsiniz.