Değişken Kullanımı Hakkında Her Şey!

Daha önce DAX 'ta değişken kullanımı ile ilgili bir yazı yazmıştım, göz atmak isterseniz buradan. Bu yazıda konuyu biraz daha genişletmek ve değişken kullanımına ait tüm bilinmesi gereken noktaları açmak istedim.

Konuyu biraz daha açmak isteyişimin bir sebebi de sık gördüğüm hatalı bir kullanım şekli.

Soru şu : Aşağıdaki iki formül de aynı sonucu vermesi lazım mantıken ama vermiyor! Niye?

Formüller şöyle:

Kategori % Niye Çalışmıyor ? := 
VAR _MevcutSatislar = [Satışlar]
VAR _SeciliKategoriler = ALLSELECTED(Kategoriler[Kategori] )
VAR _SeciliKategorilerinSatislari = CALCULATE( _MevcutSatislar , _SeciliKategoriler )
RETURN
DIVIDE( _MevcutSatislar , _SeciliKategorilerinSatislari )
Kategori % := 
DIVIDE( 
    [Satışlar] , 
    CALCULATE( [Satışlar] , ALLSELECTED( 'Kategoriler'[Kategori] ))
)
değişken kullanımı

"Düz Türkçe" ile yapmak istediğimiz şey belli: Her bir kategorinin satışının seçili kategorilerin toplam satışlarına oranını bulmak istiyoruz. Formüllerin yapısına algoritma mantığı olarak baktığımızda gerçekten de aynı! [Satışlar] metriği bir değişkene atanmış, seçili kategorilerin listesi bir değişkene atanmış, seçili kategorilerin satışları bir değişkene atanış, en sonda da basit bir bölme işlemi yapılmış.

İstediğimiz sonucu veren diğer metriğin tek farkı, bunların değişken kullanmadan yapılmış olması.

DAX 'taki her konseptin basit cümleleri ve basit kuralları var.

Filter context ile başlayalım tekrar. Ve bunu değişken kullanımı ile birleştirelim.

Matrisi daha da basitleştirelim, sadece istediğimiz gibi çalışmayan formül kalsın! Hatta formülü de basitleştirelim! Hiçbir şey yazmayalım formüle!

Degiskenkullanimi 2

En tepedeki ve en temel kurallardan biri : Yazdığımız tüm metriklerde, en basitinden en kompleksine kadar, rakam/değer gördüğümüz her bir hücre, ilgili context altında tek tek hesaplanır. Buna -eğer varsa- toplam satırı da dahildir. Kullandığımız görsel bir line chart ya da tree map de olabilir, farketmez, her bir hücre, değer/rakam gördüğümüz her bir nokta tek tek ilgili context altında hesaplanır.

Yukarıdaki matris örneğine dönecek olursak, soru işaretinin gözüktüğü 7 tane hücre var. Bu demektir ki, formüle ne yazdıysak yazdık, yazdığımız formül her bir hücre için tek tek ilgili context altında çalışacak. Yazdığımız kod toplamda 7 kez, farklı context'ler altında execute edecek!

Metriklerin tamamında, eşittir öncesinde her zaman için bir filter context vardır. Her zaman. Filter context satıra sütuna, X-Y eksenine, slicer'a ne koyduysak, bu değerlerin -genellikle- fact/transaction tablosunu filtrelemesinden sonra elimizde kalan daraltılmış-filtrelenmiş veri setidir.

Eşittirden sonra, eğer ALL ve ekürüsi gibi bir fonksiyon kullanıyorsak veya CALCULATE'in bazı kalıplarını kulllanıyorsak, bu filter context'i değiştirebiliriz.

"Düz Türkçe" ye geri dönelim: Yazacağımız formül "Bilgisayarlar" satırındaki hücre için çalışırken, eşittir öncesinde gördüğü filter context'te kategorisi "Bilgisayarlar" olan ürünlerin olduğu satışlar tablosu var.

"Cep Telefonları" ? Aynı mantık! Kategorisi cep telefonu olan ürünlerin olduğu satışlar tablosu var.

Toplam Satırı? Gene aynı mantık! Toplam Satırı jargonu aslında yanlış bir jargon, toplam satırı toplam demek değildir, bir özet satırıdır ve burada da eşittir öncesinde bir filter context vardır! Buradaki filter context 'te, slicer 'da seçili olan kategorilere ait ürünlerin olduğu satışlar tablosunu görüyor.

Eşittir öncesini açtığımıza göre değişken kullanımı detaylarına girebiliriz artık!

Değişken kullanımı, bir VAR-RETURN kalıbı.

RETURN öncesinde birden fazla sayıda değişken tanımlayabiliriz.

Aynen yukarıdaki örnekte olduğu gibi.

Her bir değişkende, formül içinde daha önceden tanımlanmış değişkenleri refere edebiliriz.

Degiskenkullanimi 3

Ama tersini yapamayız: Sıralamada sonradan tanımlanmış bir değişkeni önceki değişkenlerde refere edemeyiz!

Degiskenkullanimi 5

Değişkenler sadece ve sadece kullandığımız formül içinde refere edilebilir, başka metriklerde bu değişkenleri refere edemeyiz. Değişkenler global değildir, lokaldir! Sadece kullandığımız formül içerisinde geçerlidirler.

Değişkenlerin en önemli özelliklerinden biri, tanımlandıkları yerdeki context'e göre hesaplanırlar ve execution bitene kadar tekrar hesaplanmazlar. Yani bir kez ilgili context altında hesaplandıklarında artık sabittirler, değişmezler.

Adı "değişken" olan bir yapının kullanılırken "sabit" olması da bir ironi gibi : )

Değişkenlere sabit bir değere ek olarak tablolar da atanabilir. Yani tablo da tutabilirler.

Aşağıdaki her iki formülde aynı sonucu verir.

Satışlar ÖA := 
CALCULATE( [Satışlar] , DATEADD( 'Tarih'[Tarih] , -1 , MONTH ))
Satışlar ÖA Değişken İle = 
VAR _OncekiAyTarihlerininBulunduguTablo = DATEADD( 'Tarih'[Tarih] , -1 , MONTH )
RETURN
CALCULATE( [Satışlar] , _OncekiAyTarihlerininBulunduguTablo )
Degiskenkullanimi 6

Bu bilgiler ışığında ilk başta yazdığım formülün niye istediğimiz gibi çalışmadığına bakalım.

Degiskenkullanimi 1

"Bilgisayarlar" kategorisi için kodu ilerletelim

  1. _MevcutSatislar 'ın tanımlandığı yerdeki context'e göre değeri 2,819,093.
  2. _MevcutSatislar = 2,819,093 . Bir kez artık hesaplandı ve kodun çalışması "Bilgisayarlar" kategorisi için bitene kadar değişmeyecek. Sabit.
  3. _SeciliKategoriler = Slicer'lardan seçtiğimiz kategorilerin listesinin olduğu tablo : bilgisayar, cep telefonu ,… oyun oyuncak.
  4. CALCULATE ( _MevcutSatislar, _SeciliKategoriler ) bu durumda CALCULATE ( 2,819,093 , Bilgisayarlar, Cep Telefonları …. Oyun Oyuncak ) oldu.
  5. CALCULATE ( 2,819,093 ….. gerisine ne yazarsanız yazın sabit bir rakam olduğu için , aynı rakamı geri döndürecek.
  6. Yani _SeciliKategorilerinSatislari = 2,819,093.
  7. Bölme yaptığımız satır bu durumda 2,819,093/2,819,093 oluyor. Yani 1. Ya da 100%.

Aynı mantığı bütün hücreler için aynı şekilde uygularsak hepsinde 1 dönecek bu haliyle.

Değişkenlerin önemli cümlesini müsadenizle tekrarlayacağım: Değişkenler tanımlandıkları yerdeki context'e göre bir kez hesaplandıktan sonra execution bitene kadar sabittirler. Değişmezler.

CALCULATE ( 100 , ……), CALCULATE ( "Kırmızı", ……..) filtre olarak yazdığınız deyimler ne olursa olsun 100 ve Kırmızı döndürecek.

Bu kullanım yanlış.

Doğrusu CALCULATE ifadesine [Satışlar] metriğini vermek.

Degiskenkullanimi 7

Başka bir örnek daha yaparak bu özelliği bitirelim: Diyelim her bir kategori için ortalama ürün satışlarının üzerinde satış yaptığımız ürün sayısını bulmak istiyoruz. Buna da "Satışı İyi Ürünler" diyelim.

Satışı İyi Ürünler :=
VAR _OrtalamaUrunSatislari = AVERAGEX( 'Ürünler' , [Satışlar] )
RETURN
COUNTROWS (
    FILTER ('Ürünler' ,
        [Satışlar] > _OrtalamaUrunSatislari
    )
)
Degiskenkullanimi 8

Matrisin satırları için hesaplama yaparken, _OrtalamaUrunSatislari değişkeninin tanımlandığı yerdeki context'te, satırdaki kategoriye ait ürünlerin olduğu "Ürünler" tablosu ve bu ürünlerin satışlarının olduğu "Satışlar" tablosu var. Bu ürünlerin satış ortalamalarını bulduk ve değişkene atadık. Artık sabit. RETURN sonrası her bir hücre için hesaplama yaparken -misal Bilgisayarlar- FILTER'ın iterate ettiği "Ürünler" tablosunda satırdaki kategoriye ait ürünler var. Gördüğü her bir ürünün satışını, kategori ortalaması ile karşılaştıracak, büyük olanları sayacak.

Burada dikkatli okura bir soru sormama müsade edin : Toplam satırında gözüken "506", kategori satırlarında gözüken rakamların toplamı değil! Neden? Toplam satırında gösterdiği rakam ne? Cevaba bakmadan önce üzerinde düşünmenizi öneriyorum!

Cevaba bakacağım!

Toplam satırındaki filter context'te, seçili kategorilerin tamamı var. Dolayısıyla bu kategorilere ait ürünler ve bu ürünlerin olduğu satışlar tablosunu görüyor. Dolayısıyla _OrtalamaUrunSatislari değişkenin hesapladığı rakam, seçili tüm kategorilere ait ürünlerin satış ortalaması!

Sorduğumuz soruya göre yazdığımız kodu değiştirmemiz, modifiye etmemiz gerekebilir duruma göre. Eğer soru tüm seçili kategorilerdeki ortalama ürün satışlarına göre karşılaştırma yapmak ise, yazdığımız değişken formülünü ALLSELECTED ( ' Ürünler' ) diyerek modifiye edebiliriz.

Eğer soru yazıda olduğu gibi "her bir kategori için" ise, yazdığımız formülün toplam satırı için farklı bir mantık-formül düşünmemiz gerekebilir.

Toplam satırı toplam demek değildir demiştim, ama bazen aritmetik toplam gözükmesini isteriz. Durumun ne olduğuna göre farklı şekillerde çözülebilir. "Toplam Satırı Toplamı Göstermiyor" yazısına göz atmak isteyebilirsiniz.

Değişkenlere verilen isimler, boşluk, özel karakter, Türkçe karakter içeremiyor mevcut durumda. Sadece alpha nümerik Latin karakterler kullanabilirsiniz. Ben alışkanlık olarak tüm değişkenleri "_" ile başlatıyorum.

VAR/RETURN kalıbı içerisinde birden fazla değişken tanımlanabilir ve her bir değişkene daha önce tanımlanmış değişkenler atanabilir demiştim. Bunu biraz daha ilerletelim.

İçiçe -nested- bir şekilde, yani birden fazla VAR/RETURN kalıbını aynı formülde kullanmak mümkün. Eğer bir iterator fonksiyon içerisinde değişken(ler) kullanıyorsanız, bu değişken(ler) sadece kullandığınız iterator fonksiyon altında geçerlidir. Dışarıdan çağrılamaz!

Örneklendirelim.

Degiskenkullanimi 9

SUMX parantezi içindeki _fiyat , _miktar ve _tutar değişkenleri sadece SUMX altında çağrılabilir. Dışarıdan çağrılamaz. Misal en sondaki RETURN sonrası _tutar değişkenini refere edemeyiz.

Degiskenkullanimi 10

Son bir özellik daha, değişkenler "lazy evaluation" olarak hesaplanır. Bu şu demek, eğer RETURN sonrasında kullandığınız ifadede, veya daha öncesinde, tanımladığınız değişkenlerin hesaplanmasını gerektiren bir durum yoksa DAX bu değişkenleri hiç hesaplamaz. Bu özellikle değişkenleri yazdığımız formülleri debug ederken kolaylık sağlıyor. Sorunuzu parçalara böldünüz, her parçayı değişkenlere atadınız, değişkenlerin istediğiniz sonucu hesaplayıp hesaplamadığını anlamak için RETURN sonrası sadece ilgili değişkeni çağırabilirsiniz. Bu durumda yazdığınız kod içerisindeki diğer değişkenlerin hiçbiri hesaplanmaz.

Değişkenlerin temel kullanım amacı daha kolay/temiz kod yazmak ve performans. Performans kısmını tetikleyen en önemli etken de değişkenlerin bir kez tanımlandıkları yerde hesaplanıp aynı execution içerisinde tekrar tekrar hesaplanmaması.

Hemen her türlü formülünüzü değişkenlerle yazmaya alışmanızı önereceğim!

Yazıdaki modeli -bloga üyeyseniz- indirebilirsiniz.

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

Bloga sosyal medya hesabınızla hızlı üyelik-giriş için ilgili ikonu tıklayabilirsiniz.