Expanded Tables – Giriş

Expanded Tables, DAX'ın üzerinde koştuğu en önemli konseptlerden biri. Aslında davranışını neredeyse biliyoruz, hem ilişkilerin çalışma şeklinden dolayı, hem de RELATED gibi fonksiyonlardan dolayı. Diğer temel konseptlere de göz atmak isterseniz buradan: Filter Context, Row Context, Context Transition.

Modeldeki tablolar ve ilişkiler aşağıdaki gibi:

Çok basit SUM alan temel [Satışlar] metriği ile birlikte Alt Kategorilerin satışlarını bir matrise düşürüyorum. Aynı matrise, ilgili Alt Kategori'nin Kategori'sini getirmesi – umuduyla – amacıyla da aşağıdaki gibi bir metrik yazıyorum:

Seçili Kategori := SELECTEDVALUE( 'Ürün Kategorileri'[Kategori] )


Gelen bir şey yok!

Matristeki her bir Alt Kategori satırının (matrisin satırlarına Ürün Kodu vs de koyabilirdik) oluşturduğu ilgili filter context,  ilişkiler boyunca ok yönünde Kategoriler tablosuna uygulanamadığı için tüm kategorileri görüyor , SELECTEDVALUE fonksiyonu da  sadece tek bir değer olması durumunda çalıştığı için  bir şey döndürmüyor!

[Seçili Kategori] metriğinin gördüğü listeyi CONCATENATEX ile bulabiliriz:

Seçili Kategori Metriği Neyi Görüyor := 
CONCATENATEX( 'Ürün Kategorileri' ; [Seçili Kategori] ; "-")

Bunu çözmenin yollarından biri, 'Ürünler', 'Ürün Alt Kategorileri' ve 'Ürün Kategorileri' tabloları arasındaki ilişkileri çift yönlü (bi-directional) hale getirmek olabilir. Bu yöntem çalışır fakat -neredeyse- hiçbir zaman için  tablolar arası ilişkileri kalıcı olarak çift yönlü yapmanızı önermiyorum . Çift yönlü ilişkilerle ilgili önceki yazıya göz atmak isterseniz buradan.

Kalıcı olarak ilişkileri değiştirmektense aşağıdaki gibi bir metrik yazabiliriz.

Seçili Kategori_CROSSFILTER := 
CALCULATE( SELECTEDVALUE( 'Ürün Kategorileri'[Kategori] ) ;
         CROSSFILTER(
             'Ürünler'[Ürün Alt Kategori ID] ; 
             'Ürün Alt Kategorileri'[Ürün Alt Kategori ID] ; 
             Both ) ;
         CROSSFILTER(
             'Ürün Alt Kategorileri'[Ürün Kategori ID] ; 
             'Ürün Kategorileri'[Ürün Kategori ID] ; 
             Both )
)

Yani tablolar arasındaki ilişkileri lazım olduğu zaman, sadece gerekli metrikte çift yönlü hale getirebiliriz.


Metrik çalışıyor, ufak bir kozmetik durumu var,  hiç satışı olmayan  alt kategorileri ve bunların kategorilerini de getiriyor. Düzeltmek kolay, eğer [Satışlar] metriği sıfırdan büyükse çalış diyebiliriz, [Satışlar] metriği NOT ISBLANK ise diyebiliriz ya da satışlar tablosunda kaydı varsa (yani satışı varsa, yani satışlar tablosu BOŞ değilse) çalış diyebiliriz.

Seçili Kategori_CROSSFILTER := 
IF( NOT ISEMPTY( 'Satışlar' ) ; 
    CALCULATE( SELECTEDVALUE( 'Ürün Kategorileri'[Kategori] ) ;
         CROSSFILTER(
             'Ürünler'[Ürün Alt Kategori ID] ; 
             'Ürün Alt Kategorileri'[Ürün Alt Kategori ID] ; 
             Both ) ;
         CROSSFILTER(
             'Ürün Alt Kategorileri'[Ürün Kategori ID] ; 
             'Ürün Kategorileri'[Ürün Kategori ID] ; 
             Both )
    )
)


Şu ana kadar yazdığımız metrikler tamamen çift yönlü ilişkileri kullanmak üzerineydi. Satışlar tablosundaki filtreler otomatik olarak tek yönlü olsa bile Kategoriler tablosuna ulaşabilseydi her şey kolay olacaktı.

Expanded Tables kavramı burada devreye giriyor! Önce metriğimizi yazıp matrisi görelim, sonra çalışma mantığını ve konsepti açalım:

Seçili Kategori ExpandedTables := 
CALCULATE( SELECTEDVALUE( 'Ürün Kategorileri'[Kategori] ) ; 'Satışlar' )

Yazdığımız metrikte, CALCULATE'tin  filtre parametresine  'Satışlar' tablosunun kendisini verdik ve formül çalışıyor!


Expanded Tables'ın -genişletilmiş tablolar diyeyim-  formal tanımına gelince : Expanded Tables,  temel tablonun tüm sütunlarıyla birlikte, bu temel tabloya many-to-one veya one-to-one ilişkiyle bağlı diğer tabloların ilgili tüm sütunlarını içerir .

Açalım: 'Satışlar' tablosunu temel tablo olarak alırsak, bu tabloyla many-to-one ilişkisi olan tablolar 'Ürünler',  arada kaç tane ilişki olursa olsun hiç farketmez  'Alt Kategoriler', 'Kategoriler' ve 'Tarih' tablosu. Ve 'Satışlar' tablosunun genişletilmiş halinde, yani Expanded Table'ında, satışlar tablosunun her bir satırı için ilişkilerin 1 tarafındaki tabloların ilgili sütunları var.

Yani 'Satışlar' tablosunun genişletilmiş tablosundaki sütunlar aşağıdaki gibi: 'Satışlar' + 'Ürünler' + 'Alt Kategoriler' + 'Kategoriler' + 'Tarih'


Expansion, yani genişleme  daima ilişkinin 1-tarafındaki tablolara doğru olur .

Aynı mantıkla,  'Ürünler' tablosunun expanded table'ında da , temel tablo olarak 'Ürünler' + ve ilişkinin 1-tarafındaki tabloların yani 'Alt Kategoriler' ve 'Kategoriler' tablolarının sütunları olacaktır.

Yani, her tablonun "expanded" hali, bu tablonun orijinal sütunlarını ve bu tablonun 1-tarafındaki tablolarda bulunan ilgili (RELATED) sütunları içerir.

Bu genişletilmiş tablolar  fiziki tablolar değil ,  fakat DAX'ın  neredeyse tüm semantiği  bu konsept üzerinde dönüyor!

Özellikle CALCULATE ile birlikte!

Yazdığımız metriğe geri dönüyorum: [Seçili Kategori ExpandedTables] metriğinde CALCULATE'e filtre olarak tüm 'Satışlar' tablosunu verdiğimizde, 'Satışlar' tablosunun  expanded versiyonunu görüyor , dolayısıyla doğru kategoriyi bulabiliyor.

Başka bir örnek yapalım: Satışı olan kaç tane tekil ürün ve tekil alt kategori olduğunu bulmaya çalışalım.

Satılan Tekil Ürün # := DISTINCTCOUNT( 'Satışlar'[Ürün ID] )
Satılan Tekil Alt Kategori  # YANLIŞ := DISTINCTCOUNT( 'Ürün Alt Kategorileri'[Ürün Alt Kategori ID] )


Tekil ürün sayısı formülü doğru çalışıyor, çünkü doğrudan satışlar tablosundaki Ürün ID'ye DISTINCTCOUNT çekiyoruz.

Tekil alt kategori formülü hep aynı rakamı gösteriyor, yani yanlış  çalışıyor, çünkü satışlar tablosundaki filtreleri 'Alt Kategoriler' tablosuna bu haliyle taşıyamıyor, dolayısıyla tüm alt kategorileri görüyor ve gösterdiği rakam toplam tekil alt kategori sayısı, modelde 44 tane alt kategori var!

Satışlar tablosunun expanded halini görecek şekilde CALCULATE ile metriği modifiye ettiğimizde doğru alt kategori sayısını bulacaktır.

Satılan Tekil Alt Kategori  # DOĞRU := 
CALCULATE( DISTINCTCOUNT( 'Ürün Alt Kategorileri'[Ürün Alt Kategori ID] ) ; 'Satışlar')


Son bir örnek de, literatürde "slowly changing dimensions" diye geçen duruma ilişkin yapalım.

Zamanla yavaş değişen boyut diyeceğim ama Türkçe karşılığı tam olarak nedir bilmiyorum, kabaca tarifi şöyle, aynı varlığın (entity 'nin) master tabloda birden fazla satırda karşılığı var ve anahtar sütun değerleri farklı ama aynı varlığı gösteriyor! (Tip 2 olan SCD'ye örnek veriyorum, bunların da birden fazla tipi var, en çok rastlanılan durum ise Tip 1 ve Tip 2 )

Aşağıdaki gibi bir tablo tam da bu durumu gösteriyor.


"Hasan" aslında aynı kişi, ama yaşadığı şehir değiştiği için iki farklı kaydı var, her iki kayıtta ID'ler farklı ama müşteri kodu aynı! Eğer amaç kaç tekil müşteriye satış yaptığımızı bulmaksa aşağıdaki formül yanlış çalışacaktır ve Hasan'ı iki farklı kişiymiş gibi gösterecektir.

Tekil Müşteri # YANLIŞ := DISTINCTCOUNT( 'SCD_Satışlar'[Müşteri ID] )

Doğru çalışacak metrik ise, satışların expanded tablosunu görecek şekilde Müşteri Kodu'nu tekil saymak olacaktır.

Tekil Müşteri # DOĞRU := 
CALCULATE( DISTINCTCOUNT( 'SCD_Müşteriler'[Müşteri Kodu] ) ; 'SCD_Satışlar' )


Son not olarak belirteyim, yazıda hem çift yönlü ilişkilerden hem de expanded tablolardan bahsettim ama iki durumun birbiriyle hiç ilgisi yok. Tamamen farklı iki konsept.

Expanded tables konsepti, filter context, row context ve context transition ile birlikte DAX'ın üzerinde koştuğu en temel konseptler. Başka da bir konsept yok! DAX'ı anlamanın tek yolu da, bu konseptlerin tek başlarına ve birlikte çalıştıklarında nasıl davrandıkları üzerine kafa yormaktan geçiyor.

Expanded tables konseptinin başka nüanslarına sonraki yazılarda tekrar değineceğim.

Yazıdaki modeli indirebilirsiniz.

Sadece kayıtlı üyeler görebilir. Giriş veya Üyelik için login.