EARLIER

Power BI 'daki "context" kavramı ile ilgili yazıda, iterator fonksiyonlar, verdiğimiz tablodaki  filter context üzerine  yeni bir row context ekler, bunun tek istisnası önceki context'in bir filter context değil de, aynı tablo üzerindeki bir başka row context olması durumunda gerçekleşir, bu durumda yeni yaratılan row context, önceki row context'i gizler, görmezden gelir diye yazmıştım. Bu -kafa karıştırıcı- cümleyi biraz açalım.

Ürünler tablomuzda fiyat bilgisi var.

Ürünler tablosuna, her bir satır için, satırdaki üründen daha pahalı olan ürünlerin sayısını getirmek istiyoruz diyelim. Cümleyi böyle kurdum, çünkü her bir satırdaki ürünün fiyatını tablodaki tüm ürünlerin fiyatlarıyla karşılaştırmak gerekiyor (DAX Man böyle cümle kuruyor). Temiz Türkçe'yle aslında ürünleri fiyatlarına göre sıralamak istiyoruz.

Soru, fiyatı 100'den büyük olan ürünler tarzında olsaydı çok kolaydı, basit bir koşulla ve FILTER kullanarak hesaplayabilirdik.

FILTER (' Ürünler';'Ürünler'[Birim Fiyat] > 100 )

Aynı benzetmeyi bizim bulmak istediğimize uyarlayalım:

Fiyat Sıralaması = 
COUNTROWS (
    FILTER ( 'Ürünler';
        'Ürünler'[Birim Fiyat] > Mevcut Satırdaki Ürünün Fiyatı
    )
)

FILTER, mevcut satırdaki üründen daha yüksek fiyata sahip ürünleri "Ürünler" tablosunda bulup filtreleyecek ve saymak üzere COUNTROWS'a iletecek.

Mevcut satırdaki ürünün fiyatını DAX ile nasıl yazacağız?

Burada şu anda içiçe (nested) çalışan iki tane row context var:

İlkini "Fiyat Sıralaması"  hesaplanmış sütununu oluşturarak yarattık . Her bir satır için, ne yazdıysak onu hesaplamaya çalışacak.

İkincisini FILTER diyerek yarattık. Yazdığımız kodun 4. satırındaki 'Ürünler'[Birim Fiyat],  FILTER'ın iterate ettiği satır neyse o satırın fiyatı . Bu fiyatı, hesaplanmış sütunun yarattığı dış row context'teki (outer context) ürün satırı neyse onun fiyatıyla karşılaştırmamız lazım.

Aşağıdaki formül FILTER'ın iterate ettiği satırın fiyatını kendisiyle karşılaştırır! Bu yüzden doğru sonucu vermeyecektir.

Fiyat Sıralaması = 
COUNTROWS (
    FILTER ('Ürünler';
        'Ürünler'[Birim Fiyat] > 'Ürünler'[Birim Fiyat]
    )
)

FILTER'ın yarattığı içteki (inner) row context, hesaplanmış sütunun yarattığı dıştaki (outer) row context'i görmüyor! Yazının giriş cümlesinin açıklaması bu şekilde.

Peki hesaplanmış sütunun yarattığı ilk row context'teki ürünün fiyatına nasıl erişeceğiz: Burada devreye EARLIER fonksiyonu giriyor. FILTER'ın yarattığı row context'in  öncesindeki  hesaplanmış sütunun yarattığı row context'in sütun değerine bu fonksiyonla ulaşabiliriz.

EARLIER DAX'taki -tabiri caizse- en garip fonksiyonlardan biri ve çoğu kullanıcı için korkutucu görünüyor. Aynı tablo üzerinde iç içe çalışan birden fazla row context yarattığımızda -ki bunu ya hesaplanmış sütun oluşturarak yaratırız ya da iterator fonksiyonları kullanarak- her birinin ayrı ayrı ve birbirinin altında nasıl çalıştığı üstüne biraz düşünmek lazım. Böyle düşündüğümüzde gayet kolay anlaşılıyor, bir önceki row context'in sütun değerine ulaşmamızı sağlıyor.

Aynı  tablo üzerinde ikiden fazla row context iç içe çalışıyorsa ve biz  en dıştaki  row context'in sütun değerine ulaşmak istiyorsak EARLIEST fonksiyonunu kullanabiliriz.

EARLIER fonksiyonunun bir parametresi de kaç seviye öncesine ulaşmak istiyorsak onu belirlememizi sağlıyor: EARLIER(Sütun Adı; 2) iki önceki row context'teki sütun değerini verir.

Sonuç olarak aşağıdaki fonksiyon bize istediğimiz sonucu -kısmen- verecektir.

Fiyat Sıralaması = 
COUNTROWS (
    FILTER ('Ürünler';
        'Ürünler'[Birim Fiyat] > EARLIER('Ürünler'[Birim Fiyat])
    )
)


Kısmen diyorum çünkü en yüksek fiyatlı ürün için FILTER,  COUNTROWS'a sayması için iletecek bir şey döndüremediğinden boş (blank) değer dönüyor. Bu yüzden formüle +1 eklemek lazım.

Fiyat Sıralaması = 
COUNTROWS (
    FILTER ('Ürünler';
        'Ürünler'[Birim Fiyat] > EARLIER('Ürünler'[Birim Fiyat])
    )
) + 1


Hala ufak bir sorunumuz var, aynı en yüksek fiyata sahip 14 tane ürün var, dolayısıyla ikinci en yüksek fiyata sahip olan ürünlerin sıralaması 15 ile başlıyor, halbuki 2,3 diye gitmesi daha güzel olurdu!

COUNTROWS'a saymak üzere ürün sayısı yerine tekil fiyat sayısını iletirsek bu sorunu da çözeriz.

Fiyat Sıralaması 2 = 
COUNTROWS (
    FILTER ( VALUES ('Ürünler'[Birim Fiyat]);
        'Ürünler'[Birim Fiyat] > EARLIER('Ürünler'[Birim Fiyat])
    )
) + 1


VALUES (Sütun Adı), verdiğimiz sütundaki tekil değer listesini döndürüyor, aynı fiyata sahip ürünleri saymak yerine, tekil fiyat sayısını saymış oluyoruz.

Fiyat sıralama örneğini  -veya benzerini- RANKX veya değişken kullanarak da yapmak mümkün. Bu yazıdaki temel konu, aynı tablo üzerinde birden fazla iç içe çalışan row context olması durumunda temel davranışın nasıl olduğunu aktarabilmek.

Bu yüzden yazacağım örnekleri sonraki konulara bir giriş ve not olarak değerlendirmekte fayda var.

Fiyat Sıralaması RANKX = 
RANKX (
    FILTER ('Ürünler';
        'Ürünler'[Birim Fiyat] > EARLIER ('Ürünler'[Birim Fiyat])
    );
    'Ürünler'[Birim Fiyat]; ; DESC ; Dense
)

RANKX de sonundaki X'ten anlaşılacağı üzere bir iterator. Tablosunu verip (FILTER('Ürünler'….) bu tabloda hangi değere göre sıralama yapması gerektiğini belirtiyoruz ('Ürünler'[Birim Fiyat']). Diğer parametreleri opsiyonel, sıralamanın 1'den mi başlamasını istiyoruz, büyükten küçüğe veya tersini mi sıralamak istiyoruz, eşitlik olması durumunda ne yapmasını istiyoruz bu parametrelerle belirleyebiliyoruz.


Değişken kullanımı, karmaşık formüllerin yazılmasını son derece kolaylaştıran bir yapı. DAX'ın ilk çıkan versiyonlarında yoktu, sonradan eklendi. İyiki de eklendi, çünkü bazen tam bir cankurtaran.

Fiyat Sıralaması Değişken Örneği = 
VAR
    MevcutSatirdakiUrununFiyati = 'Ürünler'[Birim Fiyat]
RETURN
    COUNTROWS (
        FILTER ( VALUES('Ürünler'[Birim Fiyat]);
            'Ürünler'[Birim Fiyat] > MevcutSatirdakiUrununFiyati
        )
    ) + 1

Yazıdaki modeli indirebilirsiniz.

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