Ana içeriğe geç
Kesit Power BI
İleri · İleri DAX

Pencere fonksiyonları (window functions) ve görsel hesaplamalar (visual calculations)

OFFSET, WINDOW, INDEX, RANK, ROWNUMBER; ORDERBY ve PARTITIONBY; görsel hesaplamalar (RUNNINGSUM, PREVIOUS, MOVINGAVERAGE).

Uygulamada aç, 10 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Neden pencere fonksiyonları?

Zaman zekâsı fonksiyonları yalnızca tarih tablosunda çalışır. “Sıradaki bir önceki satır”, “son 3 satırın ortalaması”, “grup içindeki sıra” gibi sorular sıralı her eksende sorulur. 2022'de DAX'a eklenen pencere fonksiyonları bunu SQL'deki OVER() mantığıyla çözer.

FonksiyonDöndürürSQL karşılığı
OFFSET(n, ilişki, ORDERBY(…))Geçerli satırdan n satır uzaktaki satırLAG / LEAD
WINDOW(başl, REL|ABS, bitiş, REL|ABS, ilişki, ORDERBY(…))Bir satır aralığıROWS BETWEEN
INDEX(n, ilişki, ORDERBY(…))Mutlak konumdaki satır (−1 = son)FIRST_VALUE / LAST_VALUE
RANK(ilişki, ORDERBY(…))Sıra numarasıRANK
ROWNUMBER(ilişki, ORDERBY(…))Benzersiz satır numarasıROW_NUMBER
3 Ay Pencere Ort. =
AVERAGEX(
    WINDOW(-2, REL, 0, REL, ALLSELECTED(Tarih[YilAy]), ORDERBY(Tarih[YilAy])),
    [Net Satış]
)

Kategori İçi Sıra =
RANK(
    ALLSELECTED(Urunler[Kategori], Urunler[Urun]),
    ORDERBY([Net Satış], DESC),
    PARTITIONBY(Urunler[Kategori])
)
Zaman zekâsından farkıOFFSET(-1, ALLSELECTED(Tarih[YilAy]), …) görseldeki bir önceki satırı bulur. Görselde yalnızca 2024 varsa Ocak'ın öncesi yoktur. PREVIOUSMONTH ise takvime bakar ve Aralık 2023'ü bulur. Hangisi doğru? İş sorusuna göre değişir.

Görsel hesaplamalar

Bir görseli seçip Giriş → Yeni görsel hesaplaması. Formül, modeldeki tüm veriyle değil yalnızca görselin ızgarasıyla (satırlar, sütunlar) çalışır. Model ölçüsü yazmaktan çok daha basittir:

Kümülatif = RUNNINGSUM([Net Satış])
Önceki Ay = PREVIOUS([Net Satış])
Değişim = [Net Satış] - PREVIOUS([Net Satış])
3 Ay Ort. = MOVINGAVERAGE([Net Satış], 3)
Satıra Göre Pay = DIVIDE([Net Satış], COLLAPSE([Net Satış], ROWS))

Görsel hesaplamalar modelde ölçü olarak görünmez; yalnızca o görselde yaşar. Hızlı ve basit analizler için idealdir; birden çok raporda kullanılacak iş tanımları ise model ölçüsü olmalıdır. PL-300 artık görsel hesaplama oluşturmayı ölçüyor.

Derinleş

Pencere fonksiyonlarında üç gizli varsayım

OFFSET, INDEX ve WINDOW kısa yazılır, ama arka planda üç varsayım yapar. Mülakatta da sık sorulur:

  1. Bağlam ilişkisi (relation) belirtilmezse fonksiyon, görseldeki ALLSELECTED kümesini kullanır. Yani dilimleyiciyle gizlenen aylar “önceki ay” sayılmaz.
  2. ORDERBY belirtilmezse sıralama, ilişkideki sütunların kendi sırasıdır. Ay adına göre (“Ağu, Ara, Eki…”) sıralanan bir sütunda “önceki ay” alfabetik önceki ay olur. Her zaman sayısal bir sıralama sütunu (YilAy, AyNo) kullan.
  3. PARTITIONBY belirtilmezse tüm satırlar tek bir bölümdür. “Her yıl kendi içinde ilk ay” gibi sorular için yılı bölüm olarak vermek gerekir.
Önceki Ay Satış =
CALCULATE([Net Satış], OFFSET(-1, ALLSELECTED(Tarih[YilAy]), ORDERBY(Tarih[YilAy], ASC)))

Zaman zekâsı fonksiyonlarından (PREVIOUSMONTH, DATEADD) farkı: pencere fonksiyonları bir takvim bilmez. İlişki olarak olgu tablosundan türetilmiş bir liste verirsen (ör. SUMMARIZE(Satislar, Tarih[YilAy])), satış olmayan ay listede yer almaz ve OFFSET onu atlayıp bir önceki “mevcut” aya bakar. Takvim aylarına göre karşılaştırma gerekiyorsa zaman zekâsı, “bir önceki satır” gerekiyorsa pencere fonksiyonu doğru araçtır.

Uzmanlaş

İlişki parametresi: ALL mı, ALLSELECTED mı?

Finans raporunda dilimleyicide 2024 seçili. Aylık tabloda "önceki ay" sütunu Ocak 2024'te boş; finans müdürü Aralık 2023'ü görmek istiyor. Üç yazımı karşılaştıralım:

YazımOcak 2024Neden?
OFFSET(-1, ALLSELECTED(Tarih[YilAy]), ...)boşİlişkide yalnızca 2024 ayları var; Ocak'ın öncesi yok
OFFSET(-1, ALL(Tarih[YilAy]), ...)boşOFFSET 2023-12'yi bulur, ama dilimleyicinin Yil = 2024 filtresi CALCULATE'te kalır; kesişim boş
ALL(Tarih[YilAy]) + REMOVEFILTERS(Tarih[Yil])43.560,05Satır bulunur ve yıl filtresi de kaldırılır
Önceki Ay (Dilimleyiciden Bağımsız) =
CALCULATE(
    [Net Satış],
    OFFSET(-1, ALL(Tarih[YilAy]), ORDERBY(Tarih[YilAy], ASC)),
    REMOVEFILTERS(Tarih[Yil])
)

Mekanizma: OFFSET bir tablo satırı döndürür (YilAy = "2023-12"). CALCULATE bu satırı yalnızca ilişkinin sütunlarına filtre olarak koyar ve YilAy'daki eski filtrenin yerine geçer. Aynı tablonun başka sütunlarındaki filtreler (Yil, Ceyrek) olduğu gibi kalır. İkinci satırdaki hata pencere fonksiyonuna özgü değildir; filtre bağlamının sıradan kuralıdır.

Hangisi doğru? "Görseldeki bir önceki satır" isteniyorsa ALLSELECTED; "takvimdeki bir önceki ay" isteniyorsa zaman zekâsı (PREVIOUSMONTH) daha sadedir. Üçüncü yazım, takvim tablosu olmayan sıralı eksenlerde (ör. sprint numarası) işe yarar.

PARTITIONBY ve ilişkideki sütunlar

Satış ekibi "yıl içinde bir önceki ay" istiyor: Ocak, geçen yılın Aralık'ına bağlanmamalı. Bölüm (partition) sütunu ilişkinin içinde ya da ilişkiyle bağlı bir tabloda olmalıdır. En açık yol, Yil'i ilişkiye eklemektir:

Yıl İçi Önceki Ay =
CALCULATE(
    [Net Satış],
    OFFSET(
        -1,
        ALLSELECTED(Tarih[Yil], Tarih[YilAy]),
        ORDERBY(Tarih[YilAy], ASC),
        PARTITIONBY(Tarih[Yil])
    )
)

Ocak 2023 satırı artık boştur; bölümsüz sürüm aynı hücrede Aralık 2022'yi (40.257,85 ₺) gösterir. Microsoft Learn'deki kurallar üretimde önemlidir:

  • İlişkideki her sütun için dış bağlamda bir değer olmalı. Tek değer varsa o kullanılır; birden fazla dış sütun eşleşirse hata alırsın.
  • ORDERBY ve PARTITIONBY sütunları satırları benzersiz tanımlamıyorsa fonksiyon gereken ek sütunları kendisi ekler; bulamazsa hata verir.
  • ORDERBY'da ölçü kullanırsan eşit değerler sıralamayı belirsizleştirir. "Yılın en iyi ayı" gibi sorularda ikinci bir sıralama sütunu (ORDERBY([Net Satış], DESC, Tarih[YilAy], ASC)) eşitliği bozar.

Boş aylar: pencere satır sayar, takvim saymaz

WINDOW(-2, REL, 0, REL, ...) "son üç satır" demektir; bu satırlarda veri olup olmadığına bakmaz. Veri 22 Haziran 2022'de başlıyor; ilk aylarda üç farklı cevap çıkar:

AyNet SatışAVERAGEX(pencere)Toplam / 3Tam pencere
2022-06595,00595,00198,33boş
2022-0711.916,206.255,604.170,40boş
2022-086.766,356.425,856.425,856.425,85

AVERAGEX boş ayları atlar ve tek aylık "üç ay ortalaması" üretir. Üçe bölmek eksik ayları sıfır sayar. Üçüncü yol, pencere dolu değilse hiç göstermemektir. Grafikte ilk ayların yapay biçimde yüksek ya da düşük görünmesini istemiyorsan üçüncüsü en dürüst seçenektir (alıştırmada yazacaksın). Not: Haziran 2022 yalnızca 9 günlük veri içerir; tam olmayan dönemler her pencere hesabını bozar.

Toplam satırında ne olur?

Toplam satırında YilAy'ın dış bağlamda değeri yoktur. Belgelenen kurala göre fonksiyon, dış değeri olmayan sütunların mevcut her değeri için ayrı ayrı çalışır ve dönen satırların birleşimini (union) verir. Power BI'da bu şu sonuçları doğurur:

ÖlçüToplam satırındaAnlamlı mı?
Önceki Ay (OFFSET -1)Son ay hariç tüm aylar: 1.250.772,45 − 94.000,95 = 1.156.771,50Hayır
Kümülatif (WINDOW 1 ABS … 0 REL)Tüm aylar: 1.250.772,45Tesadüfen evet
3 ay ortalaması (AVERAGEX + WINDOW)Verisi olan 31 ayın ortalaması: 40.347,50Hayır

Bu uygulamanın motoru bu durumda toplamı boş gösterir; Power BI'da yukarıdaki sayıları görürsün. Kullanıcı toplam satırını okur ve yanlış yorumlar. Üretimde toplamı açıkça yönet:

Önceki Ay (Güvenli) =
IF(
    ISINSCOPE(Tarih[YilAy]),
    CALCULATE([Net Satış], OFFSET(-1, ALLSELECTED(Tarih[YilAy]), ORDERBY(Tarih[YilAy], ASC)))
)

Görsel hesaplama mı, model ölçüsü mü?

Görsel hesaplamalar (visual calculations) aynı işi çoğu zaman tek satırda yapar: PREVIOUS([Net Satış]). Seçimi şu ölçütler belirler (Microsoft Learn'deki sınırlamalardan):

ÖlçütGörsel hesaplamaModel ölçüsü
Yeniden kullanımYalnızca o görselde; görseller arasında kopyalanamazTüm raporlar ve Excel'de analiz
Filtreleme, sıralamaGörsel hesaplamaya göre filtreleme ve sıralama desteklenmezTam destek
Dışa aktarma, paylaşımVeri dışa aktarmada sonuçlar yer almaz; panoya sabitlenemez, Web'de yayımla kullanılamazSorun yok
Dinamik biçim dizesi, RELATED, USERELATIONSHIPYokVar
Yazım kolaylığıRUNNINGSUM, PREVIOUS, MOVINGAVERAGE; eksen ve sıfırlama parametreleriOFFSET/WINDOW ile ilişki, sıralama, bölüm elle yazılır
MaliyetGörselin zaten toplanmış ızgarası üzerinde çalışırHer hücrede modelden hesaplanır

Karar: ekipçe kullanılacak bir iş tanımı (resmî "önceki ay" ya da "kümülatif" rakamı) model ölçüsü olmalı. Tek bir görselde keşif amaçlı, dışa aktarılmayacak bir hesap için görsel hesaplama daha hızlı ve daha az hatalıdır.

Performans notları

  • İlişkiyi boyut sütunlarından kur (ALLSELECTED(Tarih[YilAy]): 36 satır). Olgu tablosundan türetilmiş bir ilişki (SUMMARIZE(Satislar, ...)) hem yavaştır hem de satışı olmayan ayları atlar.
  • ORDERBY'da ölçü kullanmak, her hücrede ilişkinin her satırı için ölçüyü hesaplatır: 36 aylık eksende 36 × 36 = 1.296 ölçü değerlendirmesi. Günlük eksende 1.096 × 1.096 ≈ 1,2 milyon olur.
  • Gün düzeyinde kümülatif istiyorsan ve tarih tablon işaretliyse, DATESBETWEEN ya da tarih filtresiyle yazılan klasik kalıp çoğu zaman daha hızlıdır. İki yazımı Performans Analizi ile ölç, sonra karar ver.

Kontrol listesi

  1. "Görseldeki önceki satır" mı, "takvimdeki önceki dönem" mi? Aracı buna göre seç.
  2. İlişki dilimleyiciye uymalı mı (ALLSELECTED), yoksa onu aşmalı mı (ALL + REMOVEFILTERS)?
  3. Bölüm sütunu ilişkinin içinde mi?
  4. ORDERBY eşitlikleri bozacak ikinci bir sütun içeriyor mu?
  5. Pencere boş ya da eksik dönemlerde ne göstermeli?
  6. Toplam satırında ISINSCOPE ile ne göstereceğini açıkça belirledin mi?
Alıştırmalara geç (10)