Skip to content

Chapter 5 of 6

Advanced Patterns: Calculation Groups, RLS, and Business Metrics

Calculation groups let you define reusable transformation patterns that apply to any measure on a visual. For example, a single calculation group can define YTD, prior year, and month-to-date variants that work against whatever measure is on the visual, replacing dozens of branched measures. Inside each calculation item, SELECTEDMEASURE returns the measure originally placed on the visual and SELECTEDMEASUREFORMATSTRING reads its format string. Calculation groups are typically authored in Tabular Editor and are the modern solution to the problem of stacking multiple time intelligence filters, which cannot coexist on the same visual otherwise.

Row-level security restricts data visibility per user. In the simplest pattern, you create a role in the Modeling tab and filter a dimension table by a condition such as [Email] = USERPRINCIPALNAME(). For dynamic RLS that maps users to regions or other attributes, build a mapping table joined to your dimension and filter with [Region] IN VALUES(Users[Region]). USEROBJECTID returns the Azure AD object identifier of the current user and is preferred in cloud environments where the mapping table stores object IDs rather than email addresses. CUSTOMDATA reads a value from the embedding application, useful in OEM or embedded scenarios. Avoid bidirectional filters for RLS, because they can leak rows when multiple filter paths exist; a security table with a single-direction relationship is safer.

Many common business metrics follow well-known DAX patterns. Average order value is DIVIDE([Total Sales], DISTINCTCOUNT(Sales[OrderId])). Conversion rate divides distinct orders by distinct sessions. Sell-through rate is units sold divided by units sold plus units on hand. Days of inventory on hand divides average inventory by daily cost of goods sold, where daily COGS is total COGS divided by the distinct count of dates. Cohort retention requires a cohort index column assigned at first purchase, after which retention for month N is the count of active customers in cohort month N divided by the cohort size, often built with SUMMARIZECOLUMNS. Pareto 80/20 analysis ranks products by sales, computes cumulative sales up to each rank, and flags products whose cumulative percentage is at or below 80 percent. Dynamic segmentation uses SWITCH on a What-If parameter or a disconnected slicer table to bucket customers or products without hardcoding thresholds.

All chapters
  1. 1DAX Foundations: Columns, Measures, and Context
  2. 2CALCULATE and Filter Manipulation
  3. 3Time Intelligence Patterns
  4. 4Iterator, Table, and Ranking Functions
  5. 5Advanced Patterns: Calculation Groups, RLS, and Business Metrics
  6. 6Best Practices, Performance, and the DAX Query View

Drill it

Reading is not remembering. These come from the Power Bi Dax Common Patterns deck:

Q

Difference between calculated column and measure?

Column: computed row-by-row at refresh; stored in model; uses row context.Measure: computed at query time; aggregates; uses filter context.

Q

Basic measure: total sales?

Total Sales := SUM(Sales[Amount])

Q

Distinct customer count?

Customers := DISTINCTCOUNT(Sales[CustomerId])

Q

Sales for prior year (same period)?

PY Sales := CALCULATE([Total Sales], SAMEPERIODLASTYEAR('Date'[Date]))