DAX CALCULATE Explained: Filter Context in Plain English
CALCULATE is the most important DAX function and the most confusing. Learn filter context, row context and context transition with simple examples.
Short answer: Row-level security (RLS) filters what data each user sees by applying a DAX filter expression to a “role,” then mapping users or groups to that role in the Power BI service. Static RLS hardcodes a value per role (fine for a handful of regions); dynamic RLS looks up the signed-in user with
USERPRINCIPALNAME()against a table, so one role covers everyone.
RLS is one of those Power BI features that looks simple in the tutorial and gets complicated the moment your model has more than one fact table, a bidirectional relationship, or a manager who needs to see their whole team’s data instead of just their own row.
Static RLS defines a role with a fixed filter, written once per role:
[Region] = "EMEA"
You create one role per region, assign the right users to each role in the Power BI service, and you’re done. It works well for a small, stable list of segments.
Dynamic RLS looks up who’s logged in and filters based on a table that maps users to what they’re allowed to see:
[Region] = LOOKUPVALUE(
UserRegionMapping[Region],
UserRegionMapping[Email],
USERPRINCIPALNAME()
)
One role, one filter expression, and the actual restriction comes from a table you maintain (UserRegionMapping), not from creating a new role every time someone joins. This scales far better once you’re past a handful of segments.
EMEA Sales.Region or Customer, not the fact table directly — filters on a dimension propagate to the fact table through the relationship).[Region] = "EMEA".UserRegionMapping) with at least an email/UPN column and the value to filter by.LOOKUPVALUE if a direct relationship doesn’t fit your model shape.LOOKUPVALUE example above.This is where dynamic RLS gets more interesting than the tutorials show. Instead of matching on an exact value, build the mapping table with a manager hierarchy and use PATH functions or a filter that returns multiple allowed values (e.g. every region a manager’s team covers), rather than a single lookup value. The mechanics are the same USERPRINCIPALNAME() pattern, just with a filter expression that can return a set instead of one value.
LOOKUPVALUE or nested filter runs for every query the affected user makes. On a large model, test performance with RLS active, not just correctness — a role that works can still make the report noticeably slower.My take: start static if you have five or fewer segments and they rarely change. Move to dynamic the moment you’re creating a new role more than once a quarter, because the maintenance cost of one role plus a well-kept mapping table is lower than the cost of a growing list of near-identical roles.
Once RLS is in place, the next thing worth checking is whether it’s slowing your reports down — see Slow Power BI report? 7 fixes in order of payoff.
Part of: Power BI & DAX