DAX funkce IF a IF.EAGER

Power BI model

Funkce IF() vrací hodnotu nebo výraz uvedený ve druhém nebo třetím argumentu, v závislosti na výsledku podmínky uvedené v prvním argumentu této funkce. Pokud je výsledkem prvního argumentu hodnota TRUE, výsledkem funkce bude výraz uvedený ve druhém argumentu funkce. Pokud je výsledek prvního argumentu hodnota FALSE, výsledkem funkce bude hodnota zadaná ve třetím argumentu. Pokud třetí argument funkce vynecháme, bude tento argument nastaven automaticky na prázdnou hodnotu BLANK. 

Funkce IF.EAGER() má stejné argumenty a vrací ve všech situacích stejné výsledky. Rozdíl mezi funkcí IF() a funkcí IF.EAGER() je ve způsobu vyhodnocení jednotlivých argumentů. Za určitých okolností může být funkce IF.EAGER() vyhodnocena rychleji, výsledek těchto funkcí je ale v případě zadaní stejných argumentů a stejného kontextu vyhodnocení vždy totožný. V následující části jsou uvedeny příklady použití funkce IF() a popsán rozdíl mezi funkcí IF() a IF.EAGER()

Stejně jako v jiných programovacích jazycích, tak i v jazyku DAX můžeme vnořovat jednotlivé IF() funkce do sebe. Nicméně v těchto případech je obvykle jednodušší použít funkci SWITCH(). Příklad použití funkce SWITCH() můžete najít v samostatném příspěvku pod tímto odkazem. Benefitem pak je lepší čitelnost kódu a tedy i menší náchylnost k chybám při zachování přibližně stejného výkonu.

Příklad použití DAX funkce IF

Příklady v tomto příspěvku jsou vytvořeny ve cvičném Power BI souboru Adwenture Works DW 2020.bpix. V tomto souboru je navíc vytvořeno měřítko [Prodeje], které vrací sumu hodnot za prodané produkty v aktuálním kontextu vyhodnocení.

Měřítko:

Prodeje = SUM(Sales[Sales Amount])

První ukázkou použití funkce IF() bude vytvoření nového počítaného sloupce. Následující DAX výraz použitý pro vytvoření počítaného sloupce v tabulce 'Product' vrací pro všechny produkty v kategorii Bikes hodnotu uvedenou ve sloupci 'Product'[List Price] sníženou o 30 %. Pro všechny ostatní produkty je pak výsledkem výrazu originální katalogová cena.

Počítaný sloupec:

Katalogová cena (kola ve slevě) =
IF
(
    'Product'[Category] = "Bikes",
    'Product'[List Price] * 0.7,
    'Product'[List Price]
)

V prvním argumentu funkce IF() ověřujeme, zda produkt v aktuálním řádku patří do kategorie jízdních kol. Pokud je první argument pravdivý, pak bude hodnota v tomto řádku obsahovat katalogovou cenu vynásobenou hodnotou 0,7, jak je uvedeno ve druhém argumentu funkce IF(). Pokud produkt v aktuálním řádku není z kategorie jízdních kol, výsledkem výše uvedeného výrazu bude třetí argument funkce IF(), tedy původní hodnota ze sloupce 'Product'[List Price] bez jakékoliv úpravy. Jak je možné vidět na obrázku níže, hodnoty v novém počítaném sloupci jsou pro produkty v kategorii Bikes o 30% nižší než v původním sloupci obsahujícím katalogové ceny produktů. Pro všechny ostatní produkty je hodnota v novém počítaném sloupci stejná.

DAX funkce IF a IF.EAGER

Funkci IF() můžeme používat také v měřítku. Typickým příkladem použití funkce IF() v měřítku je ověření, v jakém kontextu je měřítko vyhodnoceno. Uvažujme například výpočet kumulativních prodejů.

Měřítko:

Kumulativní součet prodejů =
CALCULATE
(
    [Prodeje],
    'Date'[Date] <= MAX('Date'[Date])
)

Výše uvedený výpočet vrací sumu prodejů za všechny dny až po poslední den dostupný v aktuálním kontextu vyhodnocení. Pokud nové měřítko vložíme do vizuálu Matrix, spolu s měřítkem [Prodeje] a roky a měsíci v řádcích, výsledek může vypadat následovně.

DAX funkce IF a IF.EAGER 2

Problémem může být, že takto vytvořené měřítko vrací hodnoty i pro dny, které jsou sice dostupné v kalendářní tabulce, ale ve kterých ještě nedošlo k žádným prodejům.

DAX funkce IF a IF.EAGER 3

V použitém modelu jsou dostupná data za prodané produkty pouze do 15. června 2020, všechny následující dny jsou pak považovány za budoucnost. V těchto následujících dnech již tedy výsledek měřítka [Kumulativní součet prodejů] obvykle nechceme zobrazovat. Nová verze měřítka již bude vracet hodnoty pouze pro dny, ve kterých došlo k prodejům produktů.

Měřítko:

Kumulativní součet prodejů s IF() =
IF
(
    [Prodeje] > 0,
    CALCULATE
    (
        [Prodeje],
        'Date'[Date] <= MAX('Date'[Date])
    ),
    BLANK()
)

V nové verzi výpočtu kumulativních prodejů je prvním argumentem funkce IF() výraz, který vrací hodnotu TRUE v případě, že je výsledek měřítka [Prodeje] v aktuálním kontextu vyhodnocení větší než 0. Pokud ano, výsledkem měřítka bude kumulativní součet prodejů. V opačném případě bude výsledkem měřítka hodnota BLANK. V tomto případě bychom nemuseli třetí argument funkce IF() vyplňovat, protože v případě vynechání třetího argumentu ve funkci IF() je defaultní hodnota také BLANK.

DAX funkce IF a IF.EAGER 4

Jak je vidět na obrázku výše, měřítko [Kumulativní součet prodejů] i měřítko [Kumulativní součet prodejů s IF()] vrací stejné výsledky až do posledního měsíce, pro který jsou dostupné hodnoty prodejů v tabulce 'Sales'. Jakmile nejsou dostupné žádné data o prodaných produktech, nová verze výpočtu kumulativních prodejů již nevrací žádné hodnoty, zatímco původní výpočet vrací stále stejnou hodnotu i pro měsíce, ve kterých ještě nedošlo k žádným prodejům. 

Použití funkce IF() je velmi intuitivní a funguje v podstatě stejně jako v Excelu. Jediným rozdílem oproti Excelu je že v jazyku DAX musíme při vyhodnocení jednotlivých argumentů myslet na kontext, ve kterém je funkce IF() vyhodnocena. Pokud funkci IF() použijeme v kontextu řádku tabulky, můžeme v argumentech funkce přistupovat přímo k hodnotám ze sloupců použité tabulky. Pokud funkci IF() použijeme bez přítomnosti kontextu řádku, obvykle jsou v jednotlivých argumentech funkce použita měřítka nebo sumarizační funkce. 

Následuje popis rozdílu mezi funkcí IF() a IF.EAGER(). Níže uvedené informace mohou být užitečné pro autory DAX kódu, kteří mají problém s rychlostí vyhodnocení DAX výrazu a současně dokáží identifikovat, že problém s výkonem je způsobený právě funkcí IF().

Rozdíl mezi IF a IF.EAGER

Funkce IF.EAGER() vrací stejné výsledky jako funkce IF(). Rozdíl mezi těmito funkcemi spočívá ve způsobu vyhodnocení jednotlivých argumentů. Funkce IF() vyhodnotí podmínku, a na základě výsledku podmínky v prvním argumentu funkce vyhodnotí buď druhý argument funkce, nebo třetí argument funkce. 

Funkce IF.EAGER() pak vyhodnotí vždy druhý i třetí argument funkce, bez ohledu na výsledek podmínky v prvním argumentu. Až následně dojde k vyhodnocení podmínky a výběru již dříve spočítaných hodnot z druhého nebo třetího argumentu.

Důvodem existence dvou funkcí, které vrací stejné výsledky, je výkon. Za určitých okolností může být rychleji vyhodnocen výpočet s funkcí IF(), za jiných okolností pak může být rychleji vyhodnocen výpočet s použitím funkce IF.EAGER().

Kdy použít funkci IF() a kdy IF.EAGER() je velmi těžké obecně určit. Na druhou stranu, pokud pracujeme s tabulkami s pár miliony záznamy, obvykle je vyhodnocení funkce IF() i při použití v rámci iteračních funkcí obrovsky rychlé, a nemusíme řešit výběr jedné z variant.

V použitém cvičném modelu má tabulka 'Sales' 121 253 záznamů. V tomto případě je vyhodnocení následujících dvou variant výpočtů, co se týká výkonu, nerozeznatelné.

Měřítka:

(IF) Prodeje (kola ve slevě) =
SUMX
(
    Sales,
    Sales[Order Quantity] *
    IF
    (
        RELATED('Product'[Category]) = "Bikes",
        Sales[Unit Price] * 0.7,
        Sales[Unit Price]
    )
)
(IF.EAGER) Prodeje (kola ve slevě) =
SUMX
(
    Sales,
    Sales[Order Quantity] *
    IF.EAGER
    (
        RELATED('Product'[Category]) = "Bikes",
        Sales[Unit Price] * 0.7,
        Sales[Unit Price]
    )
)

Pokud obě měřítka vyhodnotíme v kontextu kategorií produktů, výpočet ani jedné z variant netrvá déle než 20 ms (20 tisícin sekundy). Rychlost výpočtu je tedy obrovská, a v tomto případě nemá smysl přemýšlet mezi jedním nebo druhým výpočtem. 

DAX funkce IF a IF.EAGER 5

Pro úplnost se ještě můžeme podívat na jinou cestu k dosažení stejného výsledku. Následující výpočet vrací stejný výsledek jako předcházející dvě měřítka, bez nutnosti použít funkci IF() nebo IF.EAGER().

Měřítko:

(Bez IF) Prodeje (kola ve slevě) =
CALCULATE
(
    SUMX
    (
        Sales,
        Sales[Order Quantity] * Sales[Unit Price] * 0.7
    ),
    KEEPFILTERS('Product'[Category] = "Bikes")
)
+
CALCULATE
(
    SUMX
    (
        Sales,
        Sales[Order Quantity] * Sales[Unit Price]
    ),
    KEEPFILTERS(NOT 'Product'[Category] = "Bikes")
)

Výše uvedený výpočet je sice delší, co se týká zápisu, v tomto specifickém případě ale bude vyhodnocen nejrychleji. Nicméně abych byl schopen určit rozdíl v rychlosti vyhodnocení všech tří měřítek, musel jsem použít tabulku 'Sales' se 40 miliony záznamů a i s takto velkou tabulkou jsou všechna tři měřítka vyhodnocena velmi rychle.

DAX funkce IF a IF.EAGER 6

Zda použít funkci IF() nebo funkci IF.EAGER() záleží na mnoha faktorech. Současně, přemýšlet mezi jednou nebo druhou variantou začíná dávat smysl až v případě práce s většími počty záznamů v tabulkách. Pokud dokážeme identifikovat, že případný pomalý výpočet je způsoben funkcí IF(), není nic jednoduššího než zkusit vytvořit podobný výpočet s funkcí IF.EAGER() a porovnat rozdíl v rychlosti vyhodnocení. Alternativně, pokud to způsob výpočtu umožňuje, můžeme funkci IF() úplně nahradit jiným typem výpočtu, ovšem pouze ve specifických případech ve kterých máme jistotu dosažení požadovaného výsledku i s použitím alternativního výpočtu.

Oficiální Microsoft dokumentace funkcí IF a IF.EAGER:
https://docs.microsoft.com/cs-cz/dax/if-function-dax
https://docs.microsoft.com/cs-cz/dax/if-eager-function-dax

Komentáře