DAX funkce IF a IF.EAGER

Úvodní obrázek

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

Funkce IF.EAGER() má stejné argumenty a vrací za všech okolností stejné výsledky. Rozdíl mezi funkcí IF() a funkcí IF.EAGER() je ve způsobu vyhodnocení, jak si vysvětlíme ve druhé části příspěvku. 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ý.

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říklady 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 za prodané produkty v aktuálním kontextu vyhodnocení.

Měřítko:

Prodeje = SUM(Sales[Sales Amount])

Pro představení funkce IF() použijeme jednoduchý příklad, ve kterém si vytvoříme nový počítaný sloupec v tabulce produktů. Následující DAX výraz použitý pro vytvoření počítaného sloupce v tabulce 'Product' vrací pro 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 tabulky patří do kategorie jízdních kol. Pokud je první argument pravdivý, pak bude hodnota v tomto řádku obsahovat katalogovou cenu vynásobenou konstantou 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 samozřejmě 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í prodeje =
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 Matice, 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

Měřítko [Kumulativní prodeje] vrací správné hodnoty až do posledního dne pro který máme k dispozici prodeje. Například v měsíci srpen 2017 můžeme vidět hodnotu prodejů za měsíc červenec a srpen 2017. V září vrací měřítko [Kumulativní prodeje] prodeje za poslední tři měsíce, a tak dále. Problémem ale 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. V reálném modelu by tedy měřítko vracelo kumulativní prodeje také pro dny v budoucnosti.

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í prodeje] obvykle nechceme zobrazovat. Nová verze měřítka již díky podmínce ve funkci  IF() bude vracet hodnoty pouze pro dny, pro které máme k dispozici záznamy o prodejích.

Měřítko:

Kumulativní prodeje (ošetření budoucnosti) =
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 je podmínka splněna, výsledkem měřítka bude kumulativní součet prodejů. V opačném případě bude výsledkem měřítka prázdná hodnota BLANK. V tomto případě bychom nemuseli třetí argument funkce IF() vůbec vyplňovat, protože v případě vynechání třetího argumentu ve funkci IF() je defaultní hodnota také prázdná hodnota BLANK.

DAX funkce IF a IF.EAGER 4

Jak je vidět na obrázku výše, měřítko [Kumulativní prodeje] i měřítko [Kumulativní prodeje (ošetření budoucnosti)] vrací stejné výsledky až do posledního měsíce, pro který jsou dostupné záznamy o prodejích 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, můžeme v argumentech funkce přistupovat přímo k hodnotám ze sloupců v iterované tabulce. Pokud funkci IF() použijeme bez přítomnosti kontextu řádku, obvykle jsou v jednotlivých argumentech funkce použita měřítka nebo funkce které vrací skalární hodnoty v aktuálním kontextu filtru

V následující části příspěvku si popíšeme rozdíl mezi funkcí IF() a IF.EAGER().

Rozdíl mezi funkcí IF a funkcí 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 vyhodnotí a vrátí buď druhý argument nebo třetí argument. 

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 připravený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 protože rychlost vyhodnocení jednotlivých výpočtů vždy závisí na mnoha různých faktorech.

Uvažujme například dvě jednoduchá měřítka, měřítko [Prodeje] a měřítko [Počet zákazníků].

Měřítka:

Prodeje = SUM(Sales[Sales Amount])

Počet zákazníků = DISTINCTCOUNT(Sales[CustomerKey])

Obě měřítka si vložíme do vizuálu Matice spolu se dny z kalendářní tabulky v řádcích vizuálu.

DAX funkce IF a IF.EAGER 5

Na měřítku [Prodeje] a měřítku [Počet zákazníků] nyní není nic speciálního. Tato měřítka nám ale poslouží pro znázornění rozdílu ve způsobu vyhodnocení funkcí IF() a IF.EAGER().

Představme si situaci, kdy bychom za určitých okolností chtěli vrátit jedno měřítko, a za jiných okolností druhé měřítko. My pro tento účel použijeme z důvodu zjednodušení celého příkladu právě měřítka [Prodeje] a [Počet zákazníků].

V novém měřítku nazvaném obecně Ukazatel budeme chtít vrátit hodnotu měřítka [Prodeje], pokud bude suma za prodeje produktů v aktuálním den vyšší než průměrná suma prodejů za všechny dny. V opačném případě budeme vracet hodnotu měřítka [Počet zákazníků]. Celý takto popsaný výpočet může vypadat například následovně.

Měřítko:

Ukazatel (IF) =
VAR PrumerneDenniProdeje =
    AVERAGEX(ALL('Date'[Date]), [Prodeje])
VAR Vypocet =
    IF
    (
        [Prodeje] > PrumerneDenniProdeje,
        [Prodeje],
        [Počet zákazníků]
    )
RETURN
    Vypocet

Podobným způsobem si můžeme vytvořit měřítko které bude vracet stejné hodnoty, ale nyní s použitím funkce IF.EAGER().

Měřítko:

Ukazatel (IF.EAGER) =
VAR PrumerneDenniProdeje =
    AVERAGEX(ALL('Date'[Date]), [Prodeje])
VAR Vypocet =
    IF.EAGER
    (
        [Prodeje] > PrumerneDenniProdeje,
        [Prodeje],
        [Počet zákazníků]
    )
RETURN
    Vypocet

Obě nová měřítka budou vracet stejné výsledky, nicméně na pozadí jsou oba výpočty vyhodnoceny jiným způsobem, a to právě díky rozdílnému způsobu vyhodnocení jednotlivých argumentů ve funkci IF() a IF.EAGER(), jak jsme si popsali v úvodu této kapitoly.

DAX funkce IF a IF.EAGER 6

Měřítka [Ukazatel (IF.EAGER)] a [Ukazatel (IF)] nemají příliš velký analytický význam. Zajímavější je ale jak rychle jsou jednotlivé varianty výpočtů vyhodnoceny. Pro zdůraznění rozdílu byl pro tento účel použit model s tabulkou 'Sales' se 40 miliony záznamy. Měřítko [Ukazatel (IF)] bylo v tomto modelu a v použitém vizuálu vyhodnoceno průměrně za 580 ms. Měřítko [Ukazatel (IF.EAGER)] pak bylo v průměru vyhodnoceno za 240 ms. To je dvojnásobný rozdíl při zachování stejných výsledků a bez nutnosti jakkoliv zasahovat do logiky výpočtu.

Zde je ale nutné si uvědomit, že funkce IF.EAGER() není samospásná a výše uvedený rozdíl zdaleka nebude stejný u všech typů výpočtů. V některých typech výpočtů bude rychleji vyhodnocena funkce IF(), v jiných typech výpočtů může být rychlejší funkce IF.EAGER(). Vždy je proto nutné změřit výkon a následně se rozhodnout pro jednu nebo druhou variantu výpočtu.

Shrnutí

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