Kontext výpočtu
Cíle kapitoly
- Pochopit rozdíl mezi řádkovým a filtrovým kontextem
- Naučit se jak CALCULATE konvertuje kontexty
- Ovládat funkce ALL, ALLSELECTED a ALLEXCEPT
- Pracovat s iterátory (SUMX, MAXX, AVERAGEX)
- Optimalizovat kód pomocí proměnných (VAR...RETURN)
Úvod do kontextu
Kontext je jedním z nejdůležitějších konceptů v DAX a Power BI. Představuje "prostředí", v kterém se výpočet provádí. Bez pochopení kontextu je nemožné psát korektní a efektivní DAX kód.
V Power BI existují dva hlavní typy kontextu:
- Řádkový kontext – vztahuje se na konkrétní řádek v tabulce, typický pro vypočítané sloupce
- Filtrový kontext – určuje, které řádky jsou zahrnuty v agregaci, typický pro míry
Řádkový kontext (Row Context)
Řádkový kontext existuje, když pracujeme s konkrétním řádkem v tabulce. Nejčastěji se vyskytuje v vypočítaných sloupcích a v iterátor funkcích.
Kdy existuje řádkový kontext?
- Při vytváření vypočítaného sloupce v tabulce
- Uvnitř funkcí SUMX, MAXX, MINX, AVERAGEX
- V RLS filtrech (Row Level Security)
- Funkce RELATED() pracuje s řádkovým kontextem
Příklad: Vypočítaný sloupec
V tabulce FactInternetSales chceme vytvořit sloupec "Celková cena":
TotalPrice = OrderQuantity * UnitPrice
Když DAX vyhodnocuje tento výraz, má přístup k hodnotám aktuálního řádku (OrderQuantity a UnitPrice). To je řádkový kontext.
RELATED() funkce
Řádkový kontext umožňuje používat funkci RELATED() pro přístup k datům z propojené tabulky:
ProductCategory = RELATED(DimProduct[Category])
Filtrový kontext (Filter Context)
Filtrový kontext určuje, které řádky jsou zahrnuty v agregaci. Uplatňuje se na úrovni měr a je určen:
- Filtry v Power BI vizuálu (slicers, filters)
- Sloupci na řádcích a sloupcích v kontingenční tabulce
- Vztahy (relationships) v datovém modelu
Příklad: Měra s filtrovým kontextem
Definujeme měru "Total Sales":
Total Sales = SUM(FactInternetSales[SalesAmount])
Pokud je vizuál filtrován na rok 2020 a kategorii "Bikes", měra se automaticky vypočítá pouze pro řádky odpovídající těmto filtrům. To je filtrový kontext.
Jak filtrový kontext funguje
Power BI aplikuje filtrový kontext automaticky:
- Uživatel vybere v sliceru hodnotu (např. rok 2020)
- DAX vytvoří filtr: [Year] = 2020
- Agregace (SUM) se provede pouze na řádcích splňujících filtr
| Aspekt | Řádkový kontext | Filtrový kontext |
|---|---|---|
| Kde se používá | Vypočítané sloupce, iterátor funkce | Míry, vizuály |
| Jak pracuje | Iteruje přes řádky v tabulce | Filtruje řádky na základě výběru |
| Funkce RELATED | Funguje ✓ | Nefunguje ✗ |
| LOOKUPVALUE | Funguje ✓ | Funguje ✓ |
Konverze kontextu (Context Transition)
Konverze kontextu je proces, kdy se řádkový kontext převede na filtrový kontext. To je klíč k pochopení funkce CALCULATE.
Jak funguje konverze
Když se měra vyhodnocuje v řádkovém kontextu (např. v iterátoru SUMX), DAX automaticky převede příslušné sloupce na filtry v filtrovém kontextu. Například:
-- ❌ ŠPATNĚ: SUM() inline nevyvolá context transition -- FactInternetSales zůstane nefiltrovaný — výsledek je Grand Total × řádků Total by Product WRONG = SUMX(DimProduct, SUM(FactInternetSales[SalesAmount])) -- ✓ SPRÁVNĚ varianta 1: volejte míru (míra spustí context transition) Total by Product = SUMX(DimProduct, [# Internet]) -- ✓ SPRÁVNĚ varianta 2: explicitní CALCULATE Total by Product v2 = SUMX(DimProduct, CALCULATE(SUM(FactInternetSales[SalesAmount])))
Context transition nastane pouze tehdy, když iterátor volá míru nebo výraz obalený v CALCULATE. Inline volání SUM(Table[Col]) uvnitř SUMX context transition nespustí — FactInternetSales zůstane nefiltrovaný a výsledek bude nesprávný (Grand Total opakovaný pro každý řádek DimProduct).
Volání míry v iterátoru spouští context transition
Míru lze zjednodušeně chápat jako výraz vyhodnocený v prostředí podobném CALCULATE — proto volání míry uvnitř SUMX/AVERAGEX spustí context transition. Důležité: toto platí pro volání míry, nikoli pro inline výrazy jako SUM():
-- Vaše definice měry: Total Sales = SUM(FactInternetSales[SalesAmount]) -- Ekvivalent v Power BI: Total Sales = CALCULATE( SUM(FactInternetSales[SalesAmount]), -- konverze řádkového kontextu na filtrový )
Funkce CALCULATE — Hlubší pohled
CALCULATE je nejdůležitější funkce v DAX pro ovládání filtru. Umožňuje přidávat, přepisovat a odstraňovat filtry v kontextu.
Syntaxe
CALCULATE( expression, filter1, filter2, ..., filterN )
Příklad: Filtr přes IN — přímý argument CALCULATE
Nejjednodušší forma filtru — predikát přímo jako argument. Power BI interně převede na list hodnot:
-- Prodeje jen pro vybrané barvy (Blue, Red, Black) -- Přímý argument CALCULATE — rychlejší než FILTER # Internet Colors = CALCULATE( [# Internet] ,'DimProduct'[Color] IN {"Blue", "Red", "Black"} )
Příklad: Stejný výsledek přes FILTER()
FILTER() iteruje celou tabulku — pomalejší, ale nutné pro komplexní podmínky (FIND, NOT, vnořené funkce):
-- Stejný výsledek jako # Internet Colors, ale přes FILTER # Internet Color Filters = CALCULATE( [# Internet] ,FILTER( DimProduct, 'DimProduct'[Color] IN {"Blue", "Red", "Black"} ) ) -- FIND: hledej slovo v textu — nutné použít FILTER -- 4. argument (notFoundValue = 0) zajistí, že FIND() nevrátí chybu # Internet Like = CALCULATE( [# Internet] ,FILTER('DimProduct' ,FIND("bike", 'DimProduct'[ModelName], 1, 0) > 0 ) )
DimProduct[Color] IN {...}). FILTER() je nutný jen pro složené podmínky přes více sloupců nebo funkce jako FIND, SEARCH, NOT(ISBLANK(...)).Jak fungují filtry v CALCULATE
- Sloupec = Hodnota – ADD: přidá filtr (v kombinaci s ostatními filtry)
- ALL(tabulka) – OVERRIDE: odstraní všechny filtry na tabulce
- ALLEXCEPT(tabulka, sloupec) – OVERRIDE: odstraní všechny filtry kromě jednoho sloupce
- FILTER(tabulka, podmínka) – ADD: přidá komplexní filtr
Příklad: CALCULATE + ALL — Bikes přes všechny sortery
ALL() odstraní filtr na sloupci Sorter, aby se Bikes počítalo bez ohledu na řazení:
-- Prodeje Bikes — ignoruj sloupec Sorter # Internet Bikes = CALCULATE( [# Internet] ,DimProduct[Category] = "Bikes" ,ALL('DimProduct'[Sorter]) )
CALCULATE([m], T[Color]="Blue", T[Color]="Red") vrátí prázdno, ne výsledek pro Red. Chování "přepíše" platí jen pro OVERRIDE funkce (ALL, REMOVEFILTERS).Funkce pro filtrování
ALL() — odstraní filtr ze sloupce
Odstraní filtr na konkrétním sloupci — umožňuje vypočítat „celkový internet" bez ohledu na aktuálně vybranou barvu:
-- Prodeje přes všechny barvy (ignoruj slicer/filtr na Color) # Internet ALL = CALCULATE( [# Internet] ,ALL(DimProduct[Color]) )
ALLSELECTED() — zachová výběr ze sliceru
Odstraní filtry z vizuálu (řádky tabulky), ale zachová výběr ze slicerů — vhodné pro „podíl ze sliceru":
-- Celkové prodeje v rámci vybraných barev (ze sliceru) -- Na rozdíl od ALL(), respektuje výběr uživatele # Internet ALLSELECTED = CALCULATE( [# Internet] ,ALLSELECTED(DimProduct[Color]) )
# Internet ALL = součet VŠECH barev# Internet ALLSELECTED = součet jen Blue+Red (výběr ze sliceru)
ALLEXCEPT() — odstraní vše kromě
-- Odstraní VŠECHNY filtry z DimProduct KROMĚ filtru na Color -- Výsledek: součet Reseller pro celou kategorii bez ohledu na ostatní filtry DimProduct # Reseller ALLEXCEPT Color = CALCULATE( [# Reseller], ALLEXCEPT('DimProduct', 'DimProduct'[Color]) )
USERELATIONSHIP — aktivace neaktivní relace
FactInternetSales má tři relace na DimDate (OrderDate, DueDate, ShipDate). Výchozí je DueDate. Pro filtr přes OrderDate aktivujeme neaktivní relaci:
-- Internet prodeje filtrované podle data objednávky (ne due date) # Internet USERELATIONSHIP = CALCULATE([# Internet] ,USERELATIONSHIP( 'DimDate'[DateKey], FactInternetSales[OrderDateKey] ) )
TREATAS — virtuální relace
TREATAS "přikládá" sloupce tabulky jako filtr na jiné sloupce. Použití: Reseller filtrovaný přes DueDate z Internet prodejů:
-- Reseller prodeje filtrované přes DueDate z Internet faktury # Reseller TREATAS = CALCULATE( [# Reseller] ,TREATAS( DISTINCT('FactInternetSales'[DueDateKey]) ,FactResellerSales[DueDateKey] ) )
Iterátor funkce
Iterátor funkce projdou tabulkou řádek po řádku a vyhodnotí výraz pro každý řádek. Vytváří řádkový kontext.
SUMX — Součet s iterací
Součet výrazů vypočítaných pro každý řádek. Nutné pro výpočty jako „cena × množství" nebo pro práci se snímky inventáře:
-- Hodnota inventáře k poslednímu dni (snapshot) -- Nejdříve najdeme MAX datum přes všechny produkty, -- pak SUMX spočítá cena * množství pro každý řádek # Inventory Balance USD = VAR _md = CALCULATE(MAX(FactProductInventory[DateKey]), ALL('DimProduct')) VAR _rslt = CALCULATE( SUMX( 'FactProductInventory' ,'FactProductInventory'[UnitCost] * 'FactProductInventory'[UnitsBalance] ), FactProductInventory[DateKey] = _md ) RETURN _rslt
_md zachytí maximální datum bez filtru (ALL). Pak CALCULATE filtruje FactProductInventory na tento jeden den a SUMX projede řádky a spočítá hodnotu inventáře.MAXX, MINX, AVERAGEX
Podobně jako SUMX, ale pro maximum, minimum a průměr:
-- Průměrná prodejní hodnota na transakci (Qty * UnitPrice per row) AVERAGEX( FactInternetSales, FactInternetSales[OrderQuantity] * FactInternetSales[UnitPrice] )
SUM vs SUMX
Klasický SUM je vlastně zjednodušené SUMX bez iterace:
-- Toto jsou ekvivalenty: SUM(FactInternetSales[SalesAmount]) SUMX( FactInternetSales, SalesAmount )
Proměnné a VAR...RETURN
Proměnné zlepšují čitelnost kódu a mohou zlepšit výkon. Proměnná zaznamenává aktuální kontext v místě deklarace.
Syntaxe VAR...RETURN
VAR MyVariable = /* výraz */ RETURN /* výraz používající MyVariable */
Příklad: Měra s proměnnými
Percentage of Total Sales = VAR CurrentSales = SUM(FactInternetSales[SalesAmount]) VAR TotalSales = CALCULATE( SUM(FactInternetSales[SalesAmount]), REMOVEFILTERS(DimProduct, DimCustomer) -- ALL() bere 1 tabulku nebo n sloupců; REMOVEFILTERS pro více tabulek ) RETURN DIVIDE(CurrentSales, TotalSales)
Proč používat proměnné?
- Čitelnost: Jméno proměnné vysvětluje, co se počítá
- Údržba: Snadnější změna kódu (modifikujete jeden výraz)
- Výkon: Výraz se vyhodnotí pouze jednou a znovu se používá
- Debugging: Snadnější identifikovat problém v komplexních vzorcích
Příklad: Komplexní měra s více proměnnými
Revenue Analysis = VAR CurrentYear = MAX(DimDate[Year]) VAR CurrentRevenue = SUM(FactInternetSales[SalesAmount]) VAR PreviousRevenue = CALCULATE( SUM(FactInternetSales[SalesAmount]), DimDate[Year] = CurrentYear - 1 ) VAR Growth = DIVIDE( CurrentRevenue - PreviousRevenue, PreviousRevenue ) RETURN Growth