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

DAX’ın iç mantığı: genişletilmiş tablolar, veri soyu ve keyfi biçimli filtreler

Bir tabloyu filtrelemenin aslında genişletilmiş tablosunu filtrelemek olduğu, ALL(Satislar) ve REMOVEFILTERS(Urunler) neyi siler, tablo filtresinin gizli etkisi, veri soyu (lineage) ve TREATAS, keyfi biçimli filtre ve KEEPFILTERS, ALLSELECTED ve gölge filtre bağlamı, CALCULATE argümanlarının değerlendirme sırası.

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ç.

“CALCULATE: filtre bağlamını değiştirmek” ve “FILTER, ALL, ALLEXCEPT, ALLSELECTED” derslerinde kuralları öğrendin. “Bağlam geçişi” dersinde satır bağlamının filtreye nasıl dönüştüğünü gördün. Bu ders, o kuralların altındaki mekanizmayı anlatıyor. Bir ölçü “bazen” yanlış sonuç veriyorsa, sebep çoğu zaman bu derste anlatılan beş şeyden biridir: genişletilmiş tablo, tablo filtresi, veri soyu, keyfi biçimli filtre ya da argüman sırası.

Anadolu Market’in finans ekibinden gelen üç gerçek soru ile başlayalım:

1 · Genişletilmiş tablo: bir tablo, bağlı olduğu boyutları da içerir

DAX motoru her tabloyu genişletilmiş (expanded) hâliyle düşünür. Bir tablonun genişletilmiş hâli, kendi sütunları artı çoka-bir ilişkilerle bir tarafına doğru ulaşabildiği bütün tabloların sütunlarıdır. Genişleme yalnızca çok taraftan bir tarafa doğru olur; tersine olmaz.

TabloGenişletilmiş hâli şunları da içerir
SatislarUrunler, Kategoriler, Musteriler, Tarih, Calisanlar, Departmanlar
UrunlerKategoriler
CalisanlarDepartmanlar
HedeflerKategoriler, Tarih
GiderlerTarih, GiderTurleri, Departmanlar
Kategoriler, Tarih, MusterilerHiçbir şey (zincirin ucundaki boyutlar)

Microsoft Learn’e göre iki ayrıntı önemli: pasif ilişkiler de genişler (Satislar[TeslimTarihi] → Tarih bağı kullanılmasa bile) ve çift yönlü filtre genişlemeyi değiştirmez. Çoka-çok kardinaliteli (sınırlı) ilişkilerde ise genişleme hiç olmaz; bunu “Çoka çok ilişkiler” dersinde göreceksin.

Altın kural: Bir tabloyu filtrelemek, onun genişletilmiş tablosunu filtrelemektir. Kategoriler[Kategori] = "Elektronik" filtresi Satislar’ı süzer, çünkü Kategoriler[Kategori] genişletilmiş Satislar’ın bir sütunudur. Aynı mantık ters yönde de geçerlidir: bir filtreyi kaldıran fonksiyon, genişletilmiş tablodaki filtreleri de kaldırır.

Sayılarla: hangi kaldırma neyi siler?

Matriste satırlarda Kategoriler[Kategori] var. Elektronik satırında beş ölçünün sonucu:

Tüm Satış (Olgu) =
CALCULATE([Net Satış], ALL(Satislar))

Tüm Satış (Ürün Boyutu) =
CALCULATE([Net Satış], REMOVEFILTERS(Urunler))

Tüm Satış (Kategori Boyutu) =
CALCULATE([Net Satış], REMOVEFILTERS(Kategoriler))

Yanlış Sütun =
CALCULATE([Net Satış], REMOVEFILTERS(Urunler[Kategori]))
ÖlçüElektronik satırıNeden?
[Net Satış]339.304 ₺Kategori filtresi Satislar’a ulaşır.
ALL(Satislar)1.250.772 ₺Kategoriler genişletilmiş Satislar’ın içinde; filtre kalkar.
REMOVEFILTERS(Urunler)1.250.772 ₺Urunler’in genişletilmiş hâli Kategoriler’i içerir; kategori filtresi de kalkar.
REMOVEFILTERS(Kategoriler)1.250.772 ₺Filtre doğrudan bu tabloda.
REMOVEFILTERS(Urunler[Kategori])339.304 ₺Görselin filtresi Kategoriler’de, bu sütunda değil; hiçbir şey kalkmaz.

Ters yönü de dene: satırlarda Urunler[Urun] varken REMOVEFILTERS(Kategoriler) ürün filtresini kaldırmaz; 27" Monitör satırı 101.986 ₺ olarak kalır. Çünkü Kategoriler’in genişletilmiş tablosunda Urunler yoktur. Satislar[Kanal] kırılımında da REMOVEFILTERS(Urunler) kanal filtresine dokunmaz: Mağaza satırı 193.600 ₺ kalır, oysa ALL(Satislar) orada da 1.250.772 ₺ verir.

Pratik kuralKaldırmak istediğin filtreyi, görselin filtrelediği sütunu ya da boyut tablosunu adıyla anarak kaldır. Olgu tablosunu (ALL(Satislar)) hedeflemek, istemediğin bütün boyut filtrelerini de siler.

2 · Tablo filtresi: görünmeyen sütunlar da filtredir

Finans ekibinin ikinci sorusu. Hedef: iptal ve iade hariç satış. [Net Satış] zaten iptalleri çıkarıyor; üzerine iade filtresi eklemek yeterli gibi görünür.

İadesiz (Deneme A) =
CALCULATE([Net Satış], Satislar[Durum] <> "İade")

İadesiz (Deneme B) =
CALCULATE([Net Satış], FILTER(Satislar, Satislar[Durum] <> "İade"))

İadesiz (Deneme C) =
CALCULATE([Net Satış], FILTER(ALL(Satislar), Satislar[Durum] <> "İade"))

İadesiz (Doğru) =
CALCULATE([Toplam Satış], Satislar[Durum] <> "İptal", Satislar[Durum] <> "İade")
DenemeToplamElektronik satırıNe oldu?
A · sütun filtresi1.250.772 ₺339.304 ₺[Net Satış] içindeki CALCULATE, Durum sütununa kendi filtresini yazar ve dıştaki “İade değil” filtresinin üzerine yazar. İadeler geri gelir.
B · FILTER(Satislar)1.169.937 ₺323.108 ₺Doğru ama yavaş. Tablo filtresi Satislar’ın bütün sütunlarını (SatirNo dahil) taşır. İçteki CALCULATE yalnızca Durum’u değiştirebilir; iade satırları SatirNo filtresiyle dışarıda kalır.
C · FILTER(ALL(Satislar))1.169.937 ₺1.169.937 ₺Toplam doğru, kırılım bozuk. ALL(Satislar) genişletilmiş tabloyu, yani kategori filtresini de siler.
Doğru · iki koşul tek CALCULATE’te1.169.937 ₺323.108 ₺Aynı sütuna iki ayrı filtre argümanı VE ile kesişir. Hızlı ve okunur.

B ile C’nin farkı tam olarak genişletilmiş tablodur. B, o anki bağlamda görünen Satislar satırlarını alır; bu satırlar zaten kategoriye göre süzülmüştür ve tablo filtresi olarak verildiğinde Kategoriler sütunları da filtrede kalır. C ise önce bütün filtreleri kaldırır, sonra satırları döndürür. Diğer yol [Net Satış] - [İade Tutarı] da aynı sonucu verir ve ekipte en kolay okunan yazımdır.

Anti-kalıp: olgu tablosunu FILTER’a vermekFILTER(Satislar, …) bütün olgu tablosunu satır satır dolaşır ve sonucu genişletilmiş tablo filtresi olarak taşır. Bir milyon satırlık modelde yavaştır ve filtre bağlamına gereksiz sütunlar ekler. Koşul tek sütundaysa sütun filtresi, birkaç sütundaysa FILTER(ALL(Satislar[Durum], Satislar[Kanal]), …) gibi yalnızca gereken sütunları al.

3 · Veri soyu (lineage): filtre hangi sütunu süzeceğini nereden bilir?

“Tablo fonksiyonları” dersinde soy bağını tanıdın. Uzman düzeyinde bilmen gereken, soyun ne zaman korunduğu, ne zaman koptuğudur.

Soy korunurSoy kopar
VALUES, DISTINCT, ALL, FILTER, TOPN, CALCULATETABLE, INTERSECT, EXCEPT sonucu: sütunlar modeldeki sütunu “hatırlar”.ADDCOLUMNS ile eklenen yeni sütun, elle yazılan tablo ({"Web", "Mobil"}), DATATABLE ve bir sütun üzerinde işlem yapan ifade (Urunler[Urun] & "").

Pazarlama sorusu: “Web’den alışveriş yapan müşteriler mağazada ne kadar harcıyor?” Önce Web müşterilerinin kümesini bul, sonra bu kümeyi filtre olarak ver:

Web Müşterilerinin Mağaza Satışı =
VAR WebMusterileri =
    CALCULATETABLE(VALUES(Satislar[MusteriNo]), Satislar[Kanal] = "Web", Satislar[Durum] <> "İptal")
RETURN
    CALCULATE([Net Satış], WebMusterileri, Satislar[Kanal] = "Mağaza")

Sonuç 158.614 ₺; tüm mağaza satışı ise 193.600 ₺. Yani mağaza cirosunun yaklaşık %82’si Web’i de kullanan müşterilerden geliyor. WebMusterileri değişkeninin soyu Satislar[MusteriNo]’yu gösterdiği için CALCULATE doğru sütunu süzer; kanal filtresi ise ayrı bir argümanla Mağaza’ya çevrilir. Filtre olgu sütununda durur. Aynı kümeyi TREATAS(WebMusterileri, Musteriler[MusteriNo]) ile boyuta taşırsan sonuç aynı kalır, ama filtre artık Musteriler üzerinden Talepler gibi diğer olgulara da ulaşır. Hangisini istediğine bilerek karar ver.

Soy koptuğunda motor tabloyu filtre olarak kullanamaz. Aşağıdaki ilk ifade ürün adlarını metne çevirdiği için soyu kaybeder; ikincisi TREATAS ile soyu geri verir ve ilk 3 ürünün satışını (217.706 ₺) döndürür:

-- Soy kopar: & "" ifadesi yeni, bağımsız bir metin sütunu üretir
CALCULATE([Net Satış], SELECTCOLUMNS(TOPN(3, VALUES(Urunler[Urun]), [Net Satış]), "U", Urunler[Urun] & ""))
İlk 3 Ürün (Soy Geri Verildi) =
CALCULATE(
    [Net Satış],
    TREATAS(SELECTCOLUMNS(TOPN(3, VALUES(Urunler[Urun]), [Net Satış]), "U", Urunler[Urun] & ""), Urunler[Urun])
)

Soy kopunca filtre ya hiçbir şeyi süzmez ya da hata verir. Teşhis için tabloyu DAX sorgu görünümünde EVALUATE ile döndür, sonra TREATAS ile hedef sütunu açıkça yaz.

4 · Keyfi biçimli filtre ve KEEPFILTERS

Kullanıcı Yıl → Ay hiyerarşisinde, Ctrl ile Aralık 2023 ve Ocak 2024’ü seçti. Bu seçim iki ayrı sütun filtresi değildir; (Yıl, Ay) çiftlerinden oluşan tek bir filtredir: {(2023, 12), (2024, 1)}. Böyle, sütunların bağımsız listelerine ayrıştırılamayan filtreye keyfi biçimli filtre (arbitrarily shaped filter) denir. Seçimin net satışı 43.560 + 47.433 = 90.993 ₺’dir.

Şimdi seçim varken şu ölçü çalışsın:

2024 Satışı = CALCULATE([Net Satış], Tarih[Yil] = 2024)
2024 Satışı (Kesişim) = CALCULATE([Net Satış], KEEPFILTERS(Tarih[Yil] = 2024))
DurumFiltre sonunda ne olur?Sonuç
Seçim (keyfi biçim){(2023, 12), (2024, 1)}90.993 ₺
2024 Satışı (üzerine yazma)Yil sütunu çiftlerden çıkarılır, geriye Ay {12, 1} kalır; yeni filtre Yil = 2024. Sonuç Aralık 2024 + Ocak 2024.141.434 ₺
2024 Satışı (Kesişim)Çiftler korunur ve Yil = 2024 ile kesişir: yalnızca (2024, 1).47.433 ₺
Biçim kaybolsaydı (iki ayrı dilimleyici)Yil {2023, 2024} × Ay {12, 1}: dört ay.198.531 ₺

Üzerine yazma sürümü, kullanıcının hiç seçmediği Aralık 2024’ü raporlar. Testte görünmez, çünkü test eden genelde tek ay seçer. Hiyerarşi seçimini Playground’da TREATAS ile kurup üç sonucu kendin görebilirsin:

Seçim (Çiftler) =
CALCULATE([Net Satış], TREATAS({(2023, "Ara"), (2024, "Oca")}, Tarih[Yil], Tarih[Ay]))
Seçimde 2024 (Üzerine Yazma) =
CALCULATE(CALCULATE([Net Satış], Tarih[Yil] = 2024), TREATAS({(2023, "Ara"), (2024, "Oca")}, Tarih[Yil], Tarih[Ay]))
Seçimde 2024 (Kesişim) =
CALCULATE(CALCULATE([Net Satış], KEEPFILTERS(Tarih[Yil] = 2024)), TREATAS({(2023, "Ara"), (2024, "Oca")}, Tarih[Yil], Tarih[Ay]))

Sırasıyla 90.993 ₺, 141.434 ₺ ve 47.433 ₺ döner.

İki savunma yolu var. Birincisi, kullanıcı seçimiyle kesişmesi gereken filtrelerde KEEPFILTERS. İkincisi, biçimi tek sütunda tutmak: dilimleyiciye ya da hiyerarşiye Tarih[YilAy] gibi birleşik bir sütun koymak. Tek sütundaki filtre başka bir sütunun filtresiyle birlikte yaşar:

Seçimde 2024 (Tek Sütun) =
CALCULATE(
    CALCULATE([Net Satış], Tarih[Yil] = 2024),
    Tarih[YilAy] IN {"2023-12", "2024-01"}
)

Sonuç 47.433 ₺: Yil filtresi YilAy filtresini silemez, çünkü ikisi farklı sütunlardır.

5 · ALLSELECTED ve gölge filtre bağlamı

Power BI bir görseli çizerken bir sorgu üretir. Dilimleyiciler ve filtre bölmesi dış filtreyi kurar; görselin satırları (gruplama) her hücre için ayrı bir filtre ekler. Motor, gruplamaya başlamadan önceki filtreyi bir kenarda saklar. Buna gölge filtre bağlamı (shadow filter context) denir: etkin değildir, hiçbir şeyi süzmez, ama ALLSELECTED onu geri çağırabilir. ALLSELECTED “seçimi” değil, son gölge filtre bağlamını geri yükler.

Dilimleyicide Elektronik, Kitap ve Spor seçili, satırlarda Kategoriler[Kategori]:

Elektronik satırıPaydaSonuç
[Seçime Göre Pay %] (ALLSELECTED)Üç kategorinin toplamı: 675.417 ₺%50,2
ALL(Kategoriler) ile payBütün kategoriler: 1.250.772 ₺%27,1
Toplam satırıALLSELECTED %100, ALL ile %54,0

Sorun, iteratörlerin de gölge bağlam kurmasıdır. ALLSELECTED kullanan bir ölçüyü SUMX, AVERAGEX ya da FILTER içinden çağırırsan, ALLSELECTED görselin değil iteratörün dolaştığı kümenin bağlamını geri yükleyebilir. Sonuç görseldeki sayıyla tutmaz ve hata ayıklaması zordur.

-- Kaçın: ALLSELECTED kullanan ölçüyü iteratör içinden çağırmak
Ortalama Seçim Payı = AVERAGEX(VALUES(Urunler[Urun]), [Seçime Göre Pay %])

6 · CALCULATE argümanları hangi sırayla değerlendirilir?

CALCULATE argümanlarını yazıldığı sırayla değil, sabit bir sırayla işler:

  1. Filtre argümanları dış bağlamda hesaplanır. VALUES, MAX, FILTER gibi ifadeler, CALCULATE’in değiştireceği bağlamı değil, çağrıldığı bağlamı görür.
  2. Bağlam geçişi olur (satır bağlamı varsa).
  3. Değiştiriciler uygulanır: ALL / REMOVEFILTERS / ALLEXCEPT, USERELATIONSHIP, CROSSFILTER.
  4. Filtre argümanları uygulanır: KEEPFILTERS ile sarılı olanlar kesişir, diğerleri aynı sütundaki filtrenin üzerine yazar.
  5. İfade yeni bağlamda hesaplanır.

Sonuç 1: sıra yazımdan bağımsızdır. Aşağıdaki iki ölçü her yıl satırında aynı sayıyı, 747.066 ₺’yi verir; REMOVEFILTERS sonra yazılsa da önce çalışır ve Yil = 2024 filtresini silemez.

2024 Sabit A = CALCULATE([Net Satış], Tarih[Yil] = 2024, REMOVEFILTERS(Tarih))
2024 Sabit B = CALCULATE([Net Satış], REMOVEFILTERS(Tarih), Tarih[Yil] = 2024)

Sonuç 2: iteratör içindeki filtre argümanı satırı görmez. Aşağıdaki hatalı ölçüde VALUES(Tarih[YilAy]) bağlam geçişinden önce, dış bağlamda hesaplanır ve yılın bütün aylarını döndürür. Her ay için yılın tamamı toplanır: 2024 satırında 747.066 ₺ yerine 12 katı, 8.964.788 ₺ çıkar.

Ay Ay Toplam (Hatalı) =
SUMX(VALUES(Tarih[YilAy]), CALCULATE([Net Satış], REMOVEFILTERS(Tarih), VALUES(Tarih[YilAy])))

Ay Ay Toplam (Doğru) =
SUMX(
    VALUES(Tarih[YilAy]),
    VAR Ay = Tarih[YilAy]
    RETURN CALCULATE([Net Satış], REMOVEFILTERS(Tarih), Tarih[YilAy] = Ay)
)

Düzeltme, satırın değerini önce bir VAR’a almaktır. “Uzman kalıplar” dersinde aylık hedefi güne dağıtırken aynı hatanın çok daha sinsi bir sürümünü göreceksin: yıllık ve aylık toplamlar doğru çıkar, yalnızca hafta düzeyinde bozulur.

Üretimde test ve kontrol listesi

Bu hatalar toplam satırında ya da tek seçimde doğru görünür. Ölçüyü DAX sorgu görünümünde en az üç bağlamda dene: toplam, tek kırılım, çoklu seçim (iki ay, iki kanal). Performans Analizi’nden sorguyu DAX Studio’ya kopyalayıp sunucu zamanlamalarına bakmak, gereksiz tablo filtresini hemen ele verir.

  1. Kaldırdığım filtre görselin filtrelediği sütun ya da boyut mu, yoksa olgu tablosunun tamamı mı?
  2. FILTER’a bir tablo veriyorsam, o tablonun genişletilmiş hâli hangi filtreleri taşıyor?
  3. İç ölçü (ör. [Net Satış]) aynı sütuna kendi filtresini yazıyor mu? Dıştaki filtrem eziliyor mu?
  4. Filtre olarak verdiğim tablonun soyu doğru model sütununu gösteriyor mu? Emin değilsem TREATAS.
  5. Kullanıcı hiyerarşide çoklu seçim yapabilir mi? Evet ise sabit filtrelerde KEEPFILTERS.
  6. ALLSELECTED kullanan ölçü bir iteratörün içinden çağrılıyor mu?
  7. İteratör içindeki CALCULATE’in filtre argümanı satırın değerini mi kullanıyor? Kullanıyorsa değeri önce VAR’a al.
Ekip için belgeÖlçünün açıklamasına (Description) “hangi filtreleri korur, hangilerini kaldırır” cümlesini yaz: “Kanal ve tarih filtresini korur; ürün ve kategori filtresini kaldırır.” Kod incelemesinde ilk sorulan soru budur ve Copilot da bu açıklamayı okur.
Alıştırmalara geç (10)