Tvorba dynamických reportů
Cíle kapitoly
- Vytvářet dynamické metriky s SWITCH
() a Selected Measure pattermNa blogu Jiřího Neorala: Field parameters pro dynamické reporty · Dynamické reporty s výběrem zobrazené metriky — dynamické metriky a filtrace v reportu
- Stavět a spravovat Calculation Groups v Tabular Editoru
- Implementovat Field Parameters pro přepínání metrik a dimenzí
- Tvořit dynamické časové kalkulace s řízeným posunem
- Nastavovat Object Level Security (OLS) pro ochranu dat
Dynamic Measures — SWITCH() a Selected Measure
Dynamic Measures umožňují uživateli vybrat z nabídky metrik a dinamicky měnit, která se zobrazí na vizualizaci.
Tabulka Dynamic — reálný selektor metrik z modelu kurzu
V modelu kurzu máme tabulku Dynamic se dvěma sloupci: ID (číslo) a Metrika (popis). Uživatel si ze sliceru vybere metriku a míra vrátí odpovídající hodnotu.
-- Tabulka Dynamic: ID | Metrika -- 1 | Internet -- 2 | Reseller -- 3 | Internet YTD -- 4 | Reseller YTD -- 5 | % Plnění TGT
Varianta 1: Staré řešení — vnořené IF + FORMAT
Původní implementace v kurzu (záměrně ošklivá — demonstruje problém s duplicitním kódem):
-- Podmínka pro formát se opakuje dvakrát! # Dynamicka 1 = IF(LEN( IF( MIN('Dynamic'[ID]) = 5, FORMAT(SWITCH(MIN('Dynamic'[ID]), 1,[# Internet], 2,[# Reseller], 3,[# Internet YTD], 4,[# Reseller YTD], 5,[% TGT]), "0.0%"), FORMAT(SWITCH(MIN('Dynamic'[ID]), 1,[# Internet], 2,[# Reseller], 3,[# Internet YTD], 4,[# Reseller YTD], 5,[% TGT]), "$#,##0") )) = 0, BLANK(), -- ... celý výraz se opakuje ještě jednou ... )
Varianta 2: Refaktorovaná s VAR
Využití proměnných eliminuje duplikaci — výraz SWITCH se vypočítá jednou:
# Dynamicka 2 = VAR _sm = MIN('Dynamic'[ID]) VAR _sw = SWITCH ( _sm, 1, [# Internet], 2, [# Reseller], 3, [# Internet YTD], 4, [# Reseller YTD], 5, [% TGT] ) VAR _swf = IF(_sm = 5, FORMAT(_sw, "0.0%"), FORMAT(_sw, "$#,##0")) VAR _rslt = IF(LEN(_swf) = 0, BLANK(), _swf) RETURN _rslt
Varianta 3: Moderní — SWITCH + formatStringDefinition
Nejčistější přístup (od 2023+): míra vrací číslo, formát je řízen dynamicky přes formatStringDefinition:
-- Míra vrací číslo — žádný FORMAT() uvnitř # Dynamicka 3 = SWITCH( MIN('Dynamic'[ID]) ,1,[# Internet] ,2,[# Reseller] ,3,[# Internet YTD] ,4,[# Reseller YTD] ,5,[% TGT] ) -- Dynamický formát (nastavuje se zvlášť v TMDL / Tabular Editor): -- formatStringDefinition = IF(MIN('Dynamic'[ID])=5,"0.0%","$#,##0")
Calculation Groups — pokročilé agregace
Calculation Groups (CG) jsou mocný mechanismus v Power BI pro aplikaci transformací na measures bez úpravy samotných measures.
Co je Calculation Group?
- Speciální tabulka v datovém modelu, která se chová jinak než standardní tabulka
- Obsahuje soubor "Calculation Items" — každá položka je transformace (YTD, PY, YoY%, atd.)
- Automaticky se aplikuje na všechny measures v modelu přes "Apply Precedence"
- Minimalizuje kód — místo 10 measures pro YTD/YoY/atd., máte jednu CG s 10 položkami
Tvorba Calculation Group v Tabular Editoru
Connect to your Power BI model nebo Analysis Services.
V Tabular Editoru klikněte na tabulku → Properties → Set "Type" na "Calculation Group".
Například "Current Month", "Previous Month", "YTD", "YoY%".
Calculation Group Time Calculations — reálný příklad z modelu kurzu
Calculation Group Time Calculations v modelu kurzu obsahuje 4 položky. Výhoda: jedna skupina pokryje LY, VLY a VLY% pro všechny míry najednou.
-- Vrátí míru beze změny SELECTEDMEASURE()
CALCULATE( SELECTEDMEASURE() ,SAMEPERIODLASTYEAR('DimDate'[Date]) )
VAR _ty = SELECTEDMEASURE() VAR _ly = CALCULATE( SELECTEDMEASURE() ,SAMEPERIODLASTYEAR('DimDate'[Date]) ) VAR _rslt = IF(ISBLANK(_ty) || ISBLANK(_ly), BLANK(), _ty - _ly) RETURN _rslt
VAR _ty = SELECTEDMEASURE() VAR _ly = CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR('DimDate'[Date])) VAR _vly = IF(ISBLANK(_ty) || ISBLANK(_ly), BLANK(), _ty - _ly) RETURN DIVIDE(_vly, _ly) -- formatStringDefinition (dynamický formát jen pro VLY%): "0.0%"
Dynamický formát (formatStringDefinition)
Pro VLY% je v modelu nastaveno formatStringDefinition = "0.0%". Ostatní položky dědí formát z vybrané míry. Tímto způsobem # Internet ukazuje v $, ale VLY% automaticky v procentech.
Field Parameters — přepínání metrik a dimenzí
Field Parameters (zavedeny v Power BI 2022) jsou uživatelsky přívětivější alternativou k Dynamic Measures. Automaticky generují možnosti výběru bez psaní SWITCH().
Tvorba Field Parameter v Power BI Desktop
Pojmenujte: "Measure Selector" (nebo podobně)
Vyberte: [Total Sales], [Total Profit], [Average Sale], atd.
Tabulku "Measure Selector" s položkami a sloupcem "Value" s odkazem na měry. Vygeneruje measure "Measure Selector" s SWITCH logikou.
Praktické použití v visualizaci
- Přidejte Field Parameter jako slicing (filtr): Uživatel si zvolí metriku → measure se dynamicky změní
- Alternativa pro dimenze: Field Parameter pro přepínání mezi regionem/zemí/podkategorií v tabulce
Measure Selector := SWITCH ( TRUE (), SELECTEDVALUE ( MeasureSelector[MeasureSelector] ) = "Total Sales", [Total Sales], SELECTEDVALUE ( MeasureSelector[MeasureSelector] ) = "Total Profit", [Total Profit] )
Dynamické časové kalkulace s řízeným posunem
Uživatelé často potřebují srovnávat různá období — minulý měsíc, loňský rok, poslední 3 měsíce. Implementujeme to přes parametry.
Parametrizovaný posun s DATEADD()
Třetí argument DATEADD musí být literálové klíčové slovo (DAY, MONTH, QUARTER, YEAR) — nelze ho předat jako textovou proměnnou. Pro přepínání typu intervalu použijte SWITCH:
-- SelectedInterval = SELECTEDVALUE z tabulky parametrů ("MONTH", "QUARTER", "YEAR") TržbyPředchozíPerioda := VAR SelectedInterval = SELECTEDVALUE ( PeriodParameters[IntervalType], "MONTH" ) RETURN SWITCH ( SelectedInterval, "MONTH", CALCULATE ( [Total Sales], DATEADD ( DimDate[Date], -1, MONTH ) ), "QUARTER", CALCULATE ( [Total Sales], DATEADD ( DimDate[Date], -1, QUARTER ) ), "YEAR", CALCULATE ( [Total Sales], DATEADD ( DimDate[Date], -1, YEAR ) ) ) -- Interval musí být literál (DAY/MONTH/QUARTER/YEAR), ne textová proměnná!
DATEADD(DimDate[Date], -1, PeriodType) kde PeriodType = "MONTH" je neplatná syntaxe — DAX nevyhodnotí textovou proměnnou jako interval a vyhodí chybu. Třetí argument musí být vždy konstanta: DAY, MONTH, QUARTER, nebo YEAR.Řízené srovnání s parametrem
Pro flexibilnější řešení vytvořte tabulku parametrů:
PeriodParameters := DATATABLE ( "PeriodName", STRING, "DaysOffset", INTEGER, { { "Today", 0 }, { "Yesterday", -1 }, { "Last Week", -7 }, { "Last Month", -30 } } ) TržbyDynamicky := VAR SelectedOffset = SELECTEDVALUE ( PeriodParameters[DaysOffset], 0 ) RETURN CALCULATE ( [Total Sales], DATEADD ( DimDate[Date], SelectedOffset, DAY ) )
Object Level Security (OLS) — ochrana sloupců
Na rozdíl od Row Level Security (RLS), která filtruje řádky, Object Level Security skrývá celé sloupce nebo tabulky před určitými uživateli.
RLS vs. OLS
| Vlastnost | RLS | OLS |
|---|---|---|
| Co filtruje? | Řádky tabulky | Sloupce/tabulky |
| Případ užití | Prodejce vidí jen své tržby | HR vidí platy, Finance ne |
| Nastavení | V Power BI, RLS tab | V Tabular Editoru |
Implementace OLS v Tabular Editoru
Model musí být v režimu Import (ne DirectQuery pro OLS).
Nastavte "Allowed" nebo "Denied" role pro tento objekt.
Uživatelé přiřazení danému role nebudou vidět skrytý sloupec.
Praktický příklad: Skrytí platů v HR sloupci
- Role "Finance": Má přístup ke sloupci [Salary]
- Role "Operations": Nemá přístup ke sloupci [Salary]
- Tabulka [Employee] zůstává viditelná (s výjimkou sloupce [Salary])
User Defined Functions a SVG vizualizace
DAX umožňuje tři způsoby, jak dosáhnout vizuální dynamiky bez Power BI vizuálů třetích stran: textové indikátory přes UNICHAR(), inline SVG obrázky generované DAX mírou, a od roku 2024 User Defined Functions (UDF) — pojmenované lambda funkce definované přímo v modelu pomocí TMDL.
2.6.1 UNICHAR indikátory — nejjednodušší varianta
Metoda nevyžaduje žádné speciální nastavení. Míra vrátí textový řetězec obsahující Unicode symbol šipky, který Power BI zobrazí jako text v kartě nebo tabulce.
| Symbol | UNICHAR() | Použití |
|---|---|---|
| ▲ | UNICHAR(9650) | Kladná odchylka |
| ▼ | UNICHAR(9660) | Záporná odchylka |
| ● | UNICHAR(9679) | Neutrální stav |
| ★ | UNICHAR(9733) | Hvězdičkové hodnocení |
| ✓ | UNICHAR(10003) | Splněno / OK |
Nevýhoda: nelze přímo podmíněně formátovat barvu textu DAX mírou — barvu šipky nastavíte přes Podmíněné formátování v Power BI (Pravidla → Pole → tato míra).
Míra kombinuje šipkový symbol s hodnotou # VTGT (variance vůči cíli) z reálného modelu kurzu:
-- # VTGT z modelu: IF(ISBLANK([# TGT]), BLANK(), [# Internet] - [# TGT]) # VTGT Trend = VAR _vtgt = [# VTGT] VAR _symbol = IF(_vtgt >= 0, UNICHAR(9650), -- ▲ UNICHAR(9660) -- ▼ ) VAR _hodnota = FORMAT(ABS(_vtgt), "#,##0") RETURN IF( ISBLANK(_vtgt), BLANK(), _symbol & " " & _hodnota )
▲ 12 450 nebo ▼ 3 200. Podmíněné formátování → Barva písma → nastavte zelenou/červenou podle stejné míry # VTGT (číselná hodnota).2.6.2 SVG progress bar — míra vrací obrázek
Power BI umí zobrazit sloupec nebo míru jako obrázek, pokud začíná prefixem data:image/svg+xml,. DAX míra sestaví celý SVG kód jako textový řetězec.
SVG je XML textový formát. Power BI akceptuje inline SVG v URI formátu — znaky jako # nesmí být v řetězci (Power BI je interpretuje jako konec URI). Řešení: pojmenované barvy (green, orange, red) místo hex kódů.
| SVG prvek | Účel |
|---|---|
<svg width='200' height='24'> | Rozměry plátna |
<rect fill='lightgray' rx='4'/> | Pozadí se zaoblenými rohy |
<rect width='_w' fill='green'/> | Výplň dle hodnoty |
<text text-anchor='middle'> | Popis uprostřed |
Míra aplikovaná na % TGT z reálného modelu (DIVIDE([# Internet], [# TGT])):
% TGT Bar = VAR _pct = [% TGT] VAR _val = MIN(1, MAX(0, _pct)) -- ořez 0–100 % VAR _w = ROUND(_val * 200, 0) -- šířka výplně (0–200 px) VAR _color = IF(_pct >= 1, "green", IF(_pct >= 0.8, "orange", "red")) VAR _label = FORMAT(_pct, "0%") RETURN IF( ISBLANK(_pct), BLANK(), "data:image/svg+xml," & "<svg xmlns='http://www.w3.org/2000/svg' width='200' height='24'>" & "<rect width='200' height='24' fill='lightgray' rx='4'/>" & "<rect width='" & _w & "' height='24' fill='" & _color & "' rx='4'/>" & "<text x='100' y='17' text-anchor='middle' font-size='12'" & " font-family='Arial' fill='white' font-weight='bold'>" & _label & "</text></svg>" )
87%. Pro zobrazení jako obrázek: v tabulkovém vizuálu nastavte sloupec → Image URL (datová kategorie na calculated column, který volá tuto míru).2.6.3 User Defined Functions (UDF) — přepočet měny
V reálném modelu kurzu je přepočet CZK implementován jako calculated column Sales Original. UDF umožní stejnou logiku zavolat z libovolné míry přes SUMX — bez duplikace kódu.
Evoluce kódu: od calculated column k UDF
Sales Original z modelu▼Calculated column v FactInternetSales — funguje díky row context, ale nelze volat z míry:
Sales Original = LOOKUPVALUE( FactCurrencyRate[EndOfDayRate], FactCurrencyRate[DateKey], FactInternetSales[OrderDateKey], FactCurrencyRate[CurrencyKey], FactInternetSales[CurrencyKey] ) * FactInternetSales[SalesAmount]
Pro každý řádek faktu vyhledá směnný kurz v FactCurrencyRate pomocí dvou klíčů (datum + měna). Problém: duplikace logiky pokud chceme totéž pro FactResellerSales.
SUMX iteruje řádky — uvnitř lambdy existuje row context a LOOKUPVALUE funguje:
# Internet CZK = SUMX( FactInternetSales, LOOKUPVALUE( FactCurrencyRate[EndOfDayRate], FactCurrencyRate[DateKey], FactInternetSales[OrderDateKey], FactCurrencyRate[CurrencyKey], FactInternetSales[CurrencyKey] ) * FactInternetSales[SalesAmount] )
Funguje, ale opakujeme tu samou logiku LOOKUPVALUE pro každou míru a každý fact table. Pokud se změní struktura FactCurrencyRate, upravujeme na mnoha místech.
ConvertToCZK▼Funkce definovaná v TMDL jako pojmenovaný expression — volatelná z libovolné míry:
/// Přepočítá částku na CZK pomocí FactCurrencyRate /// @param amount Částka v původní měně /// @param dateKey OrderDateKey (integer YYYYMMDD) /// @param currencyKey Klíč měny z FactInternetSales nebo FactResellerSales function ConvertToCZK( amount: Double, dateKey: Int64, currencyKey: Int64 ) = VAR _rate = LOOKUPVALUE( FactCurrencyRate[EndOfDayRate], FactCurrencyRate[DateKey], dateKey, FactCurrencyRate[CurrencyKey], currencyKey ) RETURN IF(ISBLANK(_rate), amount, amount * _rate)
Volání z míry — logika přepočtu je na jednom místě:
-- Internet Sales v CZK # Internet CZK = SUMX( FactInternetSales, ConvertToCZK( FactInternetSales[SalesAmount], FactInternetSales[OrderDateKey], FactInternetSales[CurrencyKey] ) ) -- Reseller Sales v CZK — stejná UDF, jiný fact table # Reseller CZK = SUMX( FactResellerSales, ConvertToCZK( FactResellerSales[SalesAmount], FactResellerSales[OrderDateKey], FactResellerSales[CurrencyKey] ) )
FactCurrencyRate = oprava pouze v UDF, ne ve všech mírách.2.6.4 UDF pro SVG — ProgressBarSVG
Stejný pattern — extrahujeme opakující se SVG kód do pojmenované funkce. Tím odděláme výpočet hodnoty od vizuální prezentace.
/// Vrátí SVG progress bar jako data URI /// @param pct Hodnota 0.0 – 1.0 (např. [% TGT]) /// @param fillColor Barva výplně: "green", "orange", "red", atd. function ProgressBarSVG(pct: Double, fillColor: String) = VAR _val = MIN(1, MAX(0, pct)) VAR _w = ROUND(_val * 200, 0) VAR _label = FORMAT(pct, "0%") RETURN "data:image/svg+xml," & "<svg xmlns='http://www.w3.org/2000/svg' width='200' height='24'>" & "<rect width='200' height='24' fill='lightgray' rx='4'/>" & "<rect width='" & _w & "' height='24' fill='" & fillColor & "' rx='4'/>" & "<text x='100' y='17' text-anchor='middle' font-size='12'" & " font-family='Arial' fill='white' font-weight='bold'>" & _label & "</text></svg>"
Míra pak volá UDF a předá hodnotu + barvu rozhodnutou v míře:
% TGT Bar v2 = VAR _pct = [% TGT] VAR _color = IF(_pct >= 1, "green", IF(_pct >= 0.8, "orange", "red")) RETURN IF( ISBLANK(_pct), BLANK(), ProgressBarSVG(_pct, _color) )
ProgressBarSVG na jednom místě — všechny míry, které ji volají, se automaticky aktualizují. Bez UDF bychom hledali a upravovali každou míru zvlášť.| Přístup | Složitost | Reusabilita | Požadavky |
|---|---|---|---|
| UNICHAR indikátor | Nízká | — | Power BI Desktop |
| SVG míra (inline) | Střední | — | Power BI Desktop |
| UDF + SUMX | Střední | ⭐⭐⭐ | Tabular Editor 3 + Premium/Fabric |
| UDF pro SVG | Nízká (volání) | ⭐⭐⭐ | Tabular Editor 3 + Premium/Fabric |