Skip to main content

dax-development

Use when the task involves writing, modifying, or reviewing DAX measures in a Power BI semantic model. Triggers: "write a DAX measure", "add YTD measure", "create time intelligence", "DAX formula", "FYTD calculation", "variance measure", "percentage measure", "fix DAX error", "DAX performance", "DIVIDE safety", "VAR/RETURN pattern", "display folder", "formatString for measure", "conditional formatting measure", "budget vs actuals".

Jump to install

Source facts

Repository
natalinio/agentic-powerbi-squad
Last source activity
April 2, 2026 at 15:38
Detected SKILL.md language
English
Stars
0
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
4 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
dax-development
description
Use when the task involves writing, modifying, or reviewing DAX measures in a Power BI semantic model. Triggers: "write a DAX measure", "add YTD measure", "create time intelligence", "DAX formula", "FYTD calculation", "variance measure", "percentage measure", "fix DAX error", "DAX performance", "DIVIDE safety", "VAR/RETURN pattern", "display folder", "formatString for measure", "conditional formatting measure", "budget vs actuals".
user-invocable
true
# Skill: DAX KPIs and Measures Development ## Prerequisites — MANDATORY Before writing ANY DAX code: 1. **Read** `.github/skills/dax-development/references/dax-patterns.md` for validated DAX patterns. 2. **Read** `.github/skills/dax-development/references/dax-pitfalls.md` — anti-hallucination guard for deprecated, non-existent, and commonly confused DAX functions. 3. **Read** `.github/references/naming-conventions.md` for measure naming rules. 4. **Search** Microsoft documentation with `microsoft_docs_search` for any DAX function you are uncertain about. 5. **Search** for code examples with `microsoft_code_sample_search` when implementing time intelligence or complex calculations. ## Input / Output | | | |---|---| | **Input** | TMDL table files, `<ProjectName>/spec/requirements_summary.md` | | **Output** | `<ProjectName>/PBIP/<ProjectName>.SemanticModel/definition/tables/_Measures.tmdl` | ## Measures Table - ALL DAX measures MUST be created inside a **disconnected table** named `_Measures`. - This table has NO data relationships — it is purely a container for measures. - The `_Measures` table TMDL is defined in `tables/_Measures.tmdl`. ## DAX Coding Standards ### Formatting - ALL DAX code MUST be formatted across **multiple lines** for readability. - Use **VAR / RETURN** pattern for all non-trivial measures. - One expression per line. - Indent consistently with tabs. ### VAR Naming — Reserved Keywords (ZERO TOLERANCE) **NEVER** use DAX reserved keywords or function names as VAR names. Power BI Desktop will report *"'<Name>' is a reserved word"* compile errors. **Forbidden VAR names** (non-exhaustive): `Variance`, `Status`, `Value`, `Date`, `Time`, `Year`, `Month`, `Day`, `Table`, `Column`, `Currency`, `Number`, `Text`, `Boolean`, `True`, `False`, `Blank`, `Error`, `Order`, `Rank`, `Index`, `Format`, `Type`, `Result`, `None`, `All`, `Filter`. **Rule**: Always use descriptive, business-context prefixed names: - `Variance` → `SalesBudgetVariance` - `Status` → `BudgetStatusValue` - `Value` → `MetricValue` - `Result` → `CalcResult` ### DAX Comments (Allowed) - **DAX expressions support comments**: `//` for single-line, `/* */` for multi-line. - Use comments to explain complex business logic, time intelligence parameters, or non-obvious calculations. ### TMDL Descriptions (`///`) - Use `///` (triple-slash) before a measure declaration to set its `Description` property. - `///` must be immediately followed by the `measure` keyword — no blank line between. - This is a TMDL feature, not a comment. It populates the measure description visible in Power BI. **Example**: ```tmdl measure 'Sales Amount FYTD' = // Calculate fiscal year-to-date sales using dynamic fiscal year parameter VAR FiscalYearEndMonth = VAR FYStartMonth = SELECTEDVALUE(Parameters[ParameterValue], "1") VAR StartMonthNum = VALUE(FYStartMonth) VAR EndMonth = IF(StartMonthNum = 1, 12, StartMonthNum - 1) RETURN FORMAT(EndMonth, "0") & "/30" // Format as MM/DD VAR FYTDSales = CALCULATE( [Sales Amount], DATESYTD(Dim_Date[Date], FiscalYearEndMonth) ) RETURN FYTDSales formatString: "#,##0.00" displayFolder: "Time Intelligence" ``` ### Example: ```tmdl measure 'Sales Amount FYTD' = VAR CurrentDate = MAX(Dim_Date[Date]) VAR FYTDSales = CALCULATE( [Sales Amount], DATESYTD(Dim_Date[Date], "6/30") ) RETURN FYTDSales formatString: #,##0.00 displayFolder: Sales lineageTag: <generate-guid> ``` ### Division Safety - **ALWAYS** use `DIVIDE(numerator, denominator, 0)` instead of `/` operator. - This prevents division-by-zero errors. ### Time Intelligence - Assume the dedicated date dimension will be marked as the official **Date Table** in the model before final Desktop validation. - That marking must be done only after the date table data has been loaded and validated as contiguous in Power BI Desktop. - Use standard time intelligence functions: - **YTD**: `TOTALYTD([Measure], Dim_Date[Date])` or `DATESYTD(Dim_Date[Date])` for fiscal year: `DATESYTD(Dim_Date[Date], "6/30")` - **Previous Year**: `CALCULATE([Measure], SAMEPERIODLASTYEAR(Dim_Date[Date]))` - **Month-over-Month**: `CALCULATE([Measure], DATEADD(Dim_Date[Date], -1, MONTH))` - **YoY Variance**: Use VAR pattern to calculate both periods, then subtract. ### ⚠️ Time Intelligence with Dynamic Parameters (CRITICAL) **WARNING**: Functions like `DATESYTD`, `TOTALYTD`, `DATESMTD`, `TOTALMTD` require **constant literal values** for year-end date parameters. They do NOT accept variables or dynamic expressions. **Example of ERROR**: ```dax ❌ WRONG (causes warning: "Only constant date value is allowed"): VAR FiscalYearEndMonth = ... // Dynamic calculation RETURN CALCULATE([Sales Amount], DATESYTD(Dim_Date[Date], FiscalYearEndMonth)) ``` **Solution**: Use manual filtering logic when parameters must be dynamic: ```dax ✅ CORRECT (supports dynamic fiscal year parameters): measure 'Sales Amount FYTD' = // Manual fiscal YTD calculation with dynamic parameter support VAR CurrentDate = MAX(Dim_Date[Date]) VAR FYStartMonth = VALUE(SELECTEDVALUE(Parameters[ParameterValue], "1")) VAR CurrentYear = YEAR(CurrentDate) VAR CurrentMonth = MONTH(CurrentDate) // Calculate current fiscal year VAR FiscalYear = IF( CurrentMonth >= FYStartMonth, CurrentYear, CurrentYear - 1 ) VAR FYStartDate = DATE(FiscalYear, FYStartMonth, 1) VAR Result = CALCULATE( [Sales Amount], FILTER( ALL(Dim_Date[Date]), Dim_Date[Date] >= FYStartDate && Dim_Date[Date] <= CurrentDate ) ) RETURN Result ``` **Functions Requiring Constant Parameters**: - `DATESYTD(dates, [year_end_date])` → year_end_date must be literal like `"6/30"` - `TOTALYTD(expression, dates, [year_end_date])` → year_end_date must be literal - `DATESMTD`, `TOTALMTD`, `DATESQTD`, `TOTALQTD` → No year-end parameter, but similar optimization constraints **When to Use Manual Logic**: - Fiscal year start month is user-selectable (parameter table) - Multiple fiscal year scenarios in same model - Dynamic calendar logic based on business rules **When `DATESYTD` is OK**: - Fixed fiscal year end (e.g., always June 30): `DATESYTD(Dim_Date[Date], "6/30")` - Calendar year only: `DATESYTD(Dim_Date[Date])` (defaults to Dec 31) ### Filter Context - Use `CALCULATE()` with explicit filter modifications. - Use `REMOVEFILTERS()` instead of `ALL()` when clearing filters on a table. - Use `KEEPFILTERS()` when you need to intersect with existing filters. - NEVER use `FILTER(ALL(...))` when `REMOVEFILTERS` + `VALUES` achieves the same result more efficiently. ### Measure Dependencies - Base measures should be defined FIRST, then composite measures reference them. - Example order: 1. `Sales Amount` (base) 2. `Sales Amount FYTD` (time intelligence on base) 3. `Sales vs Budget` (comparison using two bases) 4. `Sales vs Budget %` (percentage using DIVIDE) ## Measure Naming Conventions Follow `.github/references/naming-conventions.md`. Summary: - **Base measures**: Descriptive name — `'Sales Amount'`, `'Total Quantity'` - **Time intelligence**: Suffix with period — `'Sales Amount FYTD'`, `'Sales Amount PY'` - **Percentages**: Suffix with `%` — `'Adjusted Profit %'` - **Variance**: Suffix with `vs` — `'Sales vs Budget'`, `'Sales vs Budget %'` - **Count measures**: Prefix with `#` or `Count of` — `'# Customers'`, `'Count of Transactions'` ## Display Folders Organize measures into `displayFolder` categories: - `Sales` — revenue and volume measures - `Budget` — budget comparison measures - `Profitability` — margin and profit measures - `Time Intelligence` — YTD, PY, MoM variants ## Conditional Formatting Support For KPIs with status indicators (above/below target), create helper measures: ```dax measure 'Budget Status' = VAR SalesVal = [Sales Amount FYTD] VAR BudgetVal = [Budget Amount FYTD] VAR Ratio = DIVIDE(SalesVal, BudgetVal, 0) RETURN SWITCH( TRUE(), Ratio >= 1, "Above Target", Ratio >= 0.9, "Close to Target", "Below Target" ) lineageTag: <generate-guid> ``` ## BPA Compliance Checklist (Preventive) **CRITICAL**: Before writing ANY DAX code, review the following Best Practice Analyzer rules from `.github/skills/code-review/references/bpa-rules-reference.md`. These guidelines ensure production-quality measures. ### DAX Expression Rules (Error/Warning Severity) 1. **DAX_FULLY_QUALIFIED_COLUMNS** (Error): Column references MUST be fully qualified with table name. Unqualified columns cause ambiguity errors. ```dax ✅ DO: measure 'Total Sales' = SUM(Fact_Sales[SalesAmount]) // Fully qualified ❌ DON'T: measure 'Total Sales' = SUM([SalesAmount]) // Ambiguous if multiple tables have SalesAmount ``` 2. **DAX_DIVISION_COLUMNS** (Error): Division operations MUST use `DIVIDE()` function, NEVER `/` operator. `DIVIDE()` handles division-by-zero gracefully. ```dax ✅ DO: measure 'Profit Margin' = VAR TotalProfit = SUM(Fact_Sales[Profit]) VAR TotalRevenue = SUM(Fact_Sales[Revenue]) RETURN DIVIDE(TotalProfit, TotalRevenue, 0) // Safe with default ❌ DON'T: measure 'Profit Margin' = SUM(Fact_Sales[Profit]) / SUM(Fact_Sales[Revenue]) // Throws error if denominator = 0 ``` 3. **DAX_UNQUALIFIED_MEASURES** (Warning): Measure references SHOULD be unqualified (no table prefix). Measures are model-level objects. ```dax ✅ DO: measure 'Sales YTD' = CALCULATE([Total Sales], DATESYTD(Dim_Date[Date])) // Unqualified measure ❌ DON'T: measure 'Sales YTD' = CALCULATE([_Measures].[Total Sales], DATESYTD(Dim_Date[Date])) // Unnecessary table prefix ``` 4. **DAX_TODO_COMMENTS** (Warning): DAX expressions SHOULD NOT contain TODO/FIXME comments in production. All measures must be production-ready. ```dax ❌ DON'T: measure 'Sales Forecast' = // TODO: Implement forecasting algorithm SUM(Fact_Sales[SalesAmount]) ✅ DO: measure 'Sales Forecast' = VAR HistoricalSales = CALCULATE( SUM(Fact_Sales[SalesAmount]), DATESINPERIOD(Dim_Date[Date], MAX(Dim_Date[Date]), -12, MONTH) ) VAR GrowthRate = 0.15 RETURN HistoricalSales * (1 + GrowthRate) ``` ### Formatting Rules (Warning Severity) 5. **OBJECTS_WITH_NO_FORMAT_STRING_MEASURES** (Warning): ALL measures MUST have `formatString` property. Critical for usability in Power BI visuals. ```tmdl ✅ DO: measure 'Total Sales' = expression: ```SUM(Fact_Sales[SalesAmount])``` formatString: "$#,##0.00" // Currency format displayFolder: "Sales Metrics" ❌ DON'T: measure 'Total Sales' = expression: ```SUM(Fact_Sales[SalesAmount])``` // Missing formatString ``` **Common Format Strings**: - Currency: `"$#,##0.00"` or `"€#,##0.00"` - Percentage: `"0.00%"` - Integer count: `"#,##0"` - Decimal: `"#,##0.00"` - No decimal: `"#,##0"` ### Layout Rules (Info Severity) 6. **ORGANIZE_MEASURES_IN_DISPLAY_FOLDERS** (Info): Measures SHOULD have `displayFolder` property for logical grouping. ```tmdl ✅ DO: measure 'Total Sales' = expression: ```SUM(Fact_Sales[SalesAmount])``` formatString: "$#,##0.00" displayFolder: "Sales Metrics" // Grouped measure 'Sales YTD' = expression: ```CALCULATE([Total Sales], DATESYTD(Dim_Date[Date]))``` formatString: "$#,##0.00" displayFolder: "Sales Metrics\\Time Intelligence" // Nested folder ``` **Common Folder Patterns**: - `"Sales Metrics"` - `"Finance\\Profitability"` - `"Inventory\\Stock Levels"` - `"Time Intelligence"` - `"KPIs"` 7. **PROVIDE_DESCRIPTIONS_FOR_MEASURES** (Info): Complex measures (time intelligence, statistical calculations) SHOULD have `description` property. Appears as tooltip in Power BI. ```tmdl ✅ DO: measure 'Sales Same Period Last Year' = description: "Total sales for the same period in the previous year" expression: ``` CALCULATE([Total Sales], SAMEPERIODLASTYEAR(Dim_Date[Date])) ``` formatString: "$#,##0.00"
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub