What is DAX?
DAX (Data Analysis Expressions) is the formula language of Power BI, used to build measures, calculated columns, and advanced analytics.
Table of Contents
ToggleLearn what DAX (Data Analysis Expressions) is, why it matters, and create your first measure — the Vista way.
DAX (Data Analysis Expressions) is the formula language of Power BI, used to build measures, calculated columns, and advanced analytics.
Calculated Column → fixed per row (refresh time).
Measure → calculates on the fly (report filters).
Create a basic KPI safely with DIVIDE
to avoid divide-by-zero errors.
Profit % =
DIVIDE(
SUM(Orders[Profit]),
SUM(Orders[Sales]),
0
)
Next: Learn logical bucketing with SWITCH(TRUE())
in our
Next Steps.
/
Load the Superstore table, build your Date table, and create your first measures.
If access is restricted, publish to web or export CSV and use Get Data → Text/CSV.
Time intelligence needs a proper Date table.
Calendar =
ADDCOLUMNS(
CALENDAR( MIN('Superstore'[Order Date]), MAX('Superstore'[Order Date]) ),
"Year", YEAR([Date]),
"MonthNum", MONTH([Date]),
"MonthName", FORMAT([Date], "MMM"),
"Quarter", "Q" & FORMAT([Date], "Q")
)
Model view → relate Calendar[Date] → Superstore[Order Date] (Many-to-one, single).
Modeling → New measure:
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
Use these in Cards or a Matrix (e.g., by Region, Segment).
Clean, transform, and format text with DAX. All examples use the Superstore table. Need the data? Open Superstore Google Sheet →
Customer Upper = UPPER('Superstore'[Customer Name])
Customer Lower = LOWER('Superstore'[Customer Name])
Customer Proper = PROPER('Superstore'[Customer Name])
Use PROPER for display, UPPER for grouping consistency.
First 5 = LEFT('Superstore'[Customer Name], 5)
Last 3 = RIGHT('Superstore'[Customer Name], 3)
Middle = MID('Superstore'[Product Name], 4, 3)
Name Len = LEN('Superstore'[Customer Name])
LEFT/RIGHT are great for codes; MID helps with SKU parsing.
Region Label = "Region: " & 'Superstore'[Region]
Products (as list) =
CONCATENATEX(
VALUES('Superstore'[Product Name]),
'Superstore'[Product Name],
", "
)
Use CONCATENATEX
to roll up multiple rows into a single list.
Customer Trimmed = TRIM('Superstore'[Customer Name])
Description Clean = CLEAN('Superstore'[Product Name])
Trim/Clean before matching or joining to avoid hidden-space issues.
Dash Region = SUBSTITUTE('Superstore'[Region], " ", "-")
Mask First3 = REPLACE('Superstore'[Customer Name], 1, 3, "***")
SUBSTITUTE targets substring values; REPLACE targets by position.
Sales Label = "₹ " & FORMAT('Superstore'[Sales], "#,##0")
Great for titles/tooltips. Note: FORMAT returns text, not numbers.
Space Pos = SEARCH(" ", 'Superstore'[Customer Name], 1, -1)
Find BI = FIND("BI", "Power BI") -- case-sensitive
SEARCH is case-insensitive; FIND is case-sensitive.
Has Tech = CONTAINSSTRING('Superstore'[Product Name], "Technology")
Starts An = STARTSWITH('Superstore'[Customer Name], "An")
Ends ra = ENDSWITH('Superstore'[Customer Name], "ra")
Ideal for segmentation or conditional formatting.
Is Exact = EXACT("Power BI", "power bi") -- FALSE
Smiley = UNICHAR(9786)
Code A = UNICODE("A") -- 65
Use EXACT for strict validation scenarios.
Customers in Region =
CONCATENATEX(
VALUES('Superstore'[Customer Name]),
'Superstore'[Customer Name],
", ",
'Superstore'[Customer Name],
ASC
)
Place Region on rows, use this measure as value to see comma-lists per region.
Segment • Region =
'Superstore'[Segment] & " • " & 'Superstore'[Region]
Use in table cells or tooltips for compact descriptors.
Customer Badge =
'Superstore'[Customer Name] & UNICHAR(10) &
"Region: " & 'Superstore'[Region]
Turn on Word wrap in the visual to display line breaks.
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
KPI Label =
"Sales: ₹ " & FORMAT([Total Sales], "#,##0") & UNICHAR(10) &
"Profit: ₹ " & FORMAT([Total Profit], "#,##0")
Perfect for a two-line Card visual.
Learn to build rules and categories using IF, SWITCH, AND/OR/NOT, IN, ISBLANK, COALESCE, and more — with practical Superstore examples.
Classify each row (calculated column) or dynamic result (measure) based on a condition.
-- Calculated Column (row-by-row)
High Discount? = IF('Superstore'[Discount] >= 0.2, "High", "Normal")
-- Measure (dynamic, context aware)
Total Sales = SUM('Superstore'[Sales])
Sales Flag = IF([Total Sales] > 200000, "Target Met", "Below Target")
Use a column when the logic is fixed per row; use a measure when it must respond to filters.
Avoid errors (e.g., division by zero) with IFERROR
or DIVIDE
.
Total Quantity = SUM('Superstore'[Quantity])
Total Profit = SUM('Superstore'[Profit])
Profit per Unit (safe) =
DIVIDE([Total Profit], [Total Quantity], 0)
-- Equivalent with IFERROR (less preferred)
Profit per Unit (iferror) =
IFERROR([Total Profit] / [Total Quantity], 0)
Best practice: prefer DIVIDE
for readability and built-in error handling.
Use SWITCH(TRUE())
to test multiple conditions in order.
Total Sales = SUM('Superstore'[Sales])
Sales Performance =
SWITCH(
TRUE(),
[Total Sales] > 500000, "Excellent",
[Total Sales] > 200000, "Good",
[Total Sales] > 100000, "Average",
"Poor"
)
Order matters: the first TRUE condition returns the result.
Best for one-to-one label mapping.
Shipping Speed =
SWITCH(
'Superstore'[Ship Mode],
"Same Day", "Instant",
"First Class", "Fast",
"Second Class", "Normal",
"Standard Class","Slow",
"Unknown"
)
Great for friendly labels in visuals or tooltips.
-- Row-level (calculated column)
Profitable & High Sales? =
IF( AND('Superstore'[Profit] > 0, 'Superstore'[Sales] > 500), TRUE(), FALSE() )
-- Measure-level (context aware)
High Margin or High Sales =
IF( OR([Total Profit] > 80000, [Total Sales] > 300000), "Yes", "No")
Use AND/OR
for compound rules; NOT
to invert a condition.
Focus Regions =
IF( 'Superstore'[Region] IN {"East","West"}, "Focus", "Other" )
Cleaner than chaining multiple OR conditions.
Always True = TRUE()
Always False = FALSE()
Useful for default states or debugging filters.
Handle missing values gracefully.
Avg Discount = AVERAGE('Superstore'[Discount])
Avg Discount (no blank) =
COALESCE([Avg Discount], 0)
Discount Label =
IF( ISBLANK([Avg Discount]), "No Data", FORMAT([Avg Discount], "0.0%") )
COALESCE
returns the first non-blank — simpler than nested IFs.
Return BLANK()
to hide values in visuals.
Total Profit = SUM('Superstore'[Profit])
Profit (hide negative) =
IF([Total Profit] < 0, BLANK(), [Total Profit])
Great for decluttering charts with negative outliers.
Create segments from names and categories.
Tech Product? =
IF( CONTAINSSTRING('Superstore'[Category], "Technology"), TRUE(), FALSE() )
Starts with A? =
STARTSWITH('Superstore'[Customer Name], "A")
Ends with ra? =
ENDSWITH('Superstore'[Customer Name], "ra")
Use these in filters/conditional formatting for instant segmentation.
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
Account Badge =
SWITCH(
TRUE(),
AND([Total Sales] > 300000, [Total Profit] > 60000), "⭐ Key Account",
[Total Sales] > 150000, "Growth",
[Total Profit] < 0, "At Risk",
"Regular"
)
Drop this in a table alongside Customer/Region to highlight focus accounts.
Understand when to use a Calculated Column vs a Measure, how to “convert” logic between them, and common pitfalls. Examples use Superstore. Need data? Open Superstore Sheet →
If you had a calculated column per row, you can aggregate its logic in a measure.
Original (Calculated Column): “High Discount?”
High Discount? =
IF('Superstore'[Discount] >= 0.20, "High", "Normal")
Converted (Measure): % of rows with High Discount under current filters
High Discount Rows =
CALCULATE(
COUNTROWS('Superstore'),
'Superstore'[Discount] >= 0.20
)
Total Rows =
COUNTROWS('Superstore')
% High Discount =
DIVIDE([High Discount Rows], [Total Rows], 0)
Now the result changes with slicers (Region, Segment, Date).
Pure measure logic depends on filter context; it often cannot be “stored” per row. But you can create an equivalent column using row-level data.
Original (Measure): Profit %
Total Profit = SUM('Superstore'[Profit])
Total Sales = SUM('Superstore'[Sales])
Profit % =
DIVIDE([Total Profit], [Total Sales], 0)
Row-level approximation (Calculated Column):
Row Profit % =
DIVIDE('Superstore'[Profit], 'Superstore'[Sales], 0)
This gives a row’s margin, not the dynamic total margin. For visuals, the measure version is the correct KPI.
Instead of storing many flag columns, use a segmenting measure.
Total Sales = SUM('Superstore'[Sales])
Account Segment =
SWITCH(
TRUE(),
[Total Sales] > 300000, "⭐ Key",
[Total Sales] > 150000, "Growth",
[Total Sales] > 50000, "Emerging",
"New"
)
Use in a slicer or legend — it reacts to filters (Year, Region).
Create a small table for categories; use it in slicers without bloating your fact table.
Segments =
DATATABLE(
"Segment", STRING,
{ {"Consumer"}, {"Corporate"}, {"Home Office"} }
)
Relate to ‘Superstore’ or use in disconnected slicers with measures.
Avoid adding a column to filter visibility; let the measure return nothing.
Total Profit = SUM('Superstore'[Profit])
Profit (hide negative) =
IF([Total Profit] < 0, BLANK(), [Total Profit])
Visuals skip BLANK values — cleaner charts, simpler model.
High Discount? = IF('Superstore'[Discount] >= 0.20, "High","Normal")
Total Rows
, High Discount Rows
, % High Discount
Not always. Measures depend on filter context (report-time). A column must be computed per row at refresh. If your logic aggregates (SUM, AVERAGE), keep it as a measure.
Avoid many boolean flag columns. Prefer segmentation measures (using SWITCH(TRUE())
) — smaller models, more flexibility.
CALCULATE is the most powerful and important DAX function. It changes the filter context and lets you apply custom filters to calculations. All examples below use the Superstore dataset. Need data? Open Superstore Sheet →
CALCULATE(<expression>, <filter1>, <filter2>, ...)
<expression>
→ the measure or aggregation you want (e.g., SUM, AVERAGE).<filters>
→ conditions that modify the filter context.Total Sales = SUM('Superstore'[Sales])
Sales East =
CALCULATE(
[Total Sales],
'Superstore'[Region] = "East"
)
Always returns sales for the East region, regardless of slicers.
High Value Sales =
CALCULATE(
SUM('Superstore'[Sales]),
'Superstore'[Sales] > 1000
)
Counts only transactions with sales over ₹1000.
Furniture Sales =
CALCULATE(
[Total Sales],
'Superstore'[Category] = "Furniture"
)
Ignores slicer filters and only shows Furniture sales.
East Furniture Sales =
CALCULATE(
[Total Sales],
'Superstore'[Region] = "East",
'Superstore'[Category] = "Furniture"
)
Applies multiple filters in one formula.
Total Sales = SUM('Superstore'[Sales])
Sales Last Year =
CALCULATE(
[Total Sales],
SAMEPERIODLASTYEAR('Calendar'[Date])
)
Combines CALCULATE
with SAMEPERIODLASTYEAR
to show year-over-year comparisons.
Build calendar-aware KPIs like MTD, QTD, YTD, Prior Year, YoY%, and Rolling periods using Time Intelligence functions. Examples use Superstore with a proper Calendar table.
Calendar =
ADDCOLUMNS(
CALENDAR( MIN('Superstore'[Order Date]), MAX('Superstore'[Order Date]) ),
"Year", YEAR([Date]),
"MonthNum", MONTH([Date]),
"MonthName", FORMAT([Date], "MMM"),
"Quarter", "Q" & FORMAT([Date], "Q")
)
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
We’ll reference these in MTD/QTD/YTD and other formulas.
Put Calendar[Year] and Calendar[MonthName] on the axis of visuals. Time Intelligence works best with fields from the Calendar table.
Sales MTD =
CALCULATE(
[Total Sales],
DATESMTD('Calendar'[Date])
)
Respects current filter (e.g., a selected month/year).
Sales QTD =
CALCULATE(
[Total Sales],
DATESQTD('Calendar'[Date])
)
Accumulates from quarter start to the current date in context.
Sales YTD =
CALCULATE(
[Total Sales],
DATESYTD('Calendar'[Date])
)
Default year-end is Dec 31. For fiscal years, use
DATESYTD('Calendar'[Date], "06/30")
.
Sales PY =
CALCULATE(
[Total Sales],
SAMEPERIODLASTYEAR('Calendar'[Date])
)
Compares current context (month/quarter/YTD) with the same period last year.
YoY % =
VAR Prev = [Sales PY]
RETURN DIVIDE( [Total Sales] - Prev, Prev, 0 )
Place alongside Total Sales for growth analysis.
Sales Prev Month =
CALCULATE(
[Total Sales],
DATEADD('Calendar'[Date], -1, MONTH)
)
MoM % = DIVIDE( [Total Sales] - [Sales Prev Month], [Sales Prev Month], 0 )
Use Calendar[Year] & Calendar[MonthName] on axis for clarity.
Sales R3M =
CALCULATE(
[Total Sales],
DATESINPERIOD('Calendar'[Date], MAX('Calendar'[Date]), -3, MONTH)
)
Smooths short-term fluctuations; works with any period length.
Sales R12M =
CALCULATE(
[Total Sales],
DATESINPERIOD('Calendar'[Date], MAX('Calendar'[Date]), -12, MONTH)
)
Great for trend dashboards and seasonality insights.
Use RELATED() to fetch data from lookup tables into your fact table.
Load the demo dataset via Google Sheets (clean star schema) and follow the steps below.
Open Demo Excel (Google Sheet) →
-- In FactSales table
Customer Segment =
RELATED(DimCustomer[Segment])
Customer Region =
RELATED(DimCustomer[Region])
See segment and region directly in FactSales without merging data.
-- In FactSales
Product Category =
RELATED(DimProduct[Category])
Sub-Category =
RELATED(DimProduct[Sub-Category])
Perfect for product-level filtering and grouping.
-- In DimCustomer table
Orders Count =
COUNTROWS(RELATEDTABLE(FactSales))
Shows number of orders per customer directly in lookup table.
Customer Sales =
SUMX(
RELATEDTABLE(FactSales),
FactSales[Sales]
)
Aggregates sales for each customer in their lookup row.
Use LOOKUPVALUE() to fetch a value from another table without needing a relationship.
It’s perfect when you can’t model a relation, or you’re doing a one-off enrichment.
Dataset: Open the Google Sheet Demo →
LOOKUPVALUE(
<result_column>,
<search_column1>, <search_value1>,
[<search_column2>, <search_value2>], ...,
[<alternateResult>]
)
<result_column>
where all search pairs match.BLANK()
unless you provide alternateResult.Unlike RELATED
, LOOKUPVALUE
works even if you don’t create relationships.
Bring Region into FactSales based on Customer ID (no relationship needed).
-- Calculated Column in FactSales
Region via Lookup =
LOOKUPVALUE(
DimCustomer[Region],
DimCustomer[Customer ID], FactSales[Customer ID],
"Unknown Region" -- alternateResult (optional)
)
If a customer isn’t in DimCustomer, returns “Unknown Region”.
Disambiguate duplicates: match both Product ID and Product Name.
-- Calculated Column in FactSales
SubCategory via Lookup =
LOOKUPVALUE(
DimProduct[Sub-Category],
DimProduct[Product ID], FactSales[Product ID],
DimProduct[Product Name], RELATED(DimProduct[Product Name]), -- if relationship exists use the same value, else use Fact column
"Unknown Sub-Category"
)
Add more search pairs to force a single match and avoid the “multiple values” error.
Map a selected Product ID from a slicer (disconnected table) to a label.
Selected Product Name =
VAR selPID =
SELECTEDVALUE('SlicerProducts'[Product ID])
RETURN
LOOKUPVALUE(
DimProduct[Product Name],
DimProduct[Product ID], selPID,
"No Product Selected"
)
Great for dynamic titles/cards using disconnected slicers.
Fetch a Customer ID when you only have the name.
Customer ID by Name =
LOOKUPVALUE(
DimCustomer[Customer ID],
DimCustomer[Customer Name], "Aarav Singh",
BLANK()
)
Replace the hard-coded name with a field or parameter for dynamic behavior.
"Unknown"
to avoid BLANKs in visuals.RELATED
when a relationship is possible. Use LOOKUPVALUE
for exceptions or disconnected mappings.
KEEPFILTERS() lets you add filters inside CALCULATE
without wiping out the filters already applied by slicers or visuals.
Data source: Superstore Google Sheet →
CALCULATE([Measure], 'Table'[Column] = "X")
normally replaces the filter on that column.CALCULATE([Measure], KEEPFILTERS('Table'[Column] = "X"))
will add the filter, intersecting with what’s already selected.Optional (recommended): add a Calendar table and relate Calendar[Date] → Superstore[Order Date].
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
We’ll layer KEEPFILTERS on top of these.
User picks a Region in a slicer. You want Technology sales but keep the Region selection.
Tech Sales (keep slicers) =
CALCULATE(
[Total Sales],
KEEPFILTERS('Superstore'[Category] = "Technology")
)
Any Region/Segment/Date filter from visuals remains intact; we just add Category=Technology.
Keep user choices but count only high-value orders (Sales ≥ ₹10,000).
High Value Sales =
CALCULATE(
SUM('Superstore'[Sales]),
KEEPFILTERS('Superstore'[Sales] >= 10000)
)
Filters on Region/Date/Segment continue to apply; this adds an amount threshold.
Add Category=Furniture and Ship Mode=First Class while preserving slicers.
Furniture FirstClass Sales =
CALCULATE(
[Total Sales],
KEEPFILTERS('Superstore'[Category] = "Furniture"),
KEEPFILTERS('Superstore'[Ship Mode] = "First Class")
)
Behaves like AND logic with whatever slicers are already filtering the report.
Keep current Year/Month selections, then add a sub-filter like Sub-Category=Machines.
Sales YTD • Machines =
CALCULATE(
[Total Sales],
DATESYTD('Calendar'[Date]),
KEEPFILTERS('Superstore'[Sub-Category] = "Machines")
)
Layer period logic with product filtering without breaking user selections.
-- If a slicer sets Category = "Technology"
-- This IGNORES that and forces Furniture (replaces filter)
Furniture (replace) =
CALCULATE([Total Sales], 'Superstore'[Category] = "Furniture")
-- This KEEPS slicer filters AND adds Furniture (usually results in blank unless both intersect)
Furniture (keep) =
CALCULATE([Total Sales], KEEPFILTERS('Superstore'[Category] = "Furniture"))
If the intersection is empty (Tech ∩ Furniture), result is BLANK — that’s expected and often desired.
-- REMOVEFILTERS clears context first, then your filters apply
Furniture (clear-then-apply) =
CALCULATE(
[Total Sales],
REMOVEFILTERS('Superstore'[Category]),
'Superstore'[Category] = "Furniture"
)
KEEPFILTERS = intersect with existing filters. REMOVEFILTERS = wipe filters, then apply your own.
REMOVEFILTERS() clears filters from a column or table inside CALCULATE
so you can compute totals, baselines, and % of total — regardless of slicers or row/column context.
Dataset: Superstore Google Sheet →
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
Show each category’s % of all categories even when a Category slicer is used.
All-Category Sales =
CALCULATE(
[Total Sales],
REMOVEFILTERS('Superstore'[Category]) -- clears only Category filter
)
Category % of All =
DIVIDE([Total Sales], [All-Category Sales], 0)
Use in a matrix: Rows = Category, Values = Total Sales, Category % of All.
Completely ignore slicers (Region, Segment, Date…) to get a true grand total baseline.
Grand Total Sales =
CALCULATE(
[Total Sales],
REMOVEFILTERS('Superstore') -- clears all filters from Superstore
)
% of Grand Total =
DIVIDE([Total Sales], [Grand Total Sales], 0)
Ideal for global % KPIs, regardless of user selections.
Compare current selection to the entire year regardless of other filters.
Sales YTD (Selected) =
CALCULATE([Total Sales], DATESYTD('Calendar'[Date]))
Sales YTD (All Segments) =
CALCULATE(
[Total Sales],
DATESYTD('Calendar'[Date]),
REMOVEFILTERS('Superstore'[Segment]) -- clears Segment only
)
Selected vs All Segments % =
DIVIDE([Sales YTD (Selected)] - [Sales YTD (All Segments)], [Sales YTD (All Segments)], 0)
Use Year/Month from Calendar on the axis.
Classic pattern to avoid conflicting filters.
Furniture Sales (clear-then-apply) =
CALCULATE(
[Total Sales],
REMOVEFILTERS('Superstore'[Category]), -- wipe Category filters
'Superstore'[Category] = "Furniture" -- then apply Furniture
)
Ensures the measure reflects exactly the chosen category, ignoring slicers for Category only.
-- Nearly equivalent inside CALCULATE:
CALCULATE([Total Sales], REMOVEFILTERS('Superstore'[Category]))
CALCULATE([Total Sales], ALL('Superstore'[Category]))
Both clear filters. REMOVEFILTERS is a newer, explicit form.
-- Clear all filters on table except those columns
Sales by Region (ignore others) =
CALCULATE(
[Total Sales],
ALLEXCEPT('Superstore', 'Superstore'[Region])
)
Keeps Region context but clears Category/Segment/…
Tech Sales (keep) =
CALCULATE(
[Total Sales],
KEEPFILTERS('Superstore'[Category] = "Technology")
)
Adds filters instead of clearing them — opposite intent to REMOVEFILTERS.
REMOVEFILTERS
stabilizes baselines.Group rows and build virtual tables for measures with SUMMARIZE(). Dataset: Superstore Google Sheet →
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
Sales by Region and Category as a calculated table.
Sales by Region & Category =
SUMMARIZE(
'Superstore',
'Superstore'[Region],
'Superstore'[Category],
"Total Sales", SUM('Superstore'[Sales]),
"Total Profit", SUM('Superstore'[Profit])
)
Use this table in visuals or as a source for further modeling.
Compute Top 3 Categories by Sales under current filters.
Top 3 Category Sales =
VAR t =
SUMMARIZE(
'Superstore',
'Superstore'[Category],
"Cat Sales", [Total Sales]
)
VAR top3 =
TOPN(3, t, [Cat Sales], DESC)
RETURN
SUMX(top3, [Cat Sales])
Place on a Card; it reacts to slicers (Region/Date/Segment).
Product Profit Rank =
VAR t =
SUMMARIZE(
'Superstore',
'Superstore'[Category],
'Superstore'[Product Name],
"Total Profit", [Total Profit]
)
RETURN
RANKX(
FILTER(t, [Category] = SELECTEDVALUE('Superstore'[Category])),
[Total Profit],
,
DESC,
Dense
)
Use Category as slicer to see rankings update.
Customers per Region =
VAR t =
SUMMARIZE(
'Superstore',
'Superstore'[Region],
"Cust Count", DISTINCTCOUNT('Superstore'[Customer ID])
)
VAR total = SUMX(t, [Cust Count])
RETURN
DIVIDE(
CALCULATE([Cust Count], KEEPFILTERS(VALUES('Superstore'[Region]))),
total,
0
)
Drop Region on rows and show this measure to see each region’s share.
SUMMARIZECOLUMNS
inside Measures-- Similar to Pattern 2 but with SUMMARIZECOLUMNS (respects filter context by default)
Top 3 Category Sales (Columns) =
VAR t =
SUMMARIZECOLUMNS(
'Superstore'[Category],
"Cat Sales", [Total Sales]
)
VAR top3 = TOPN(3, t, [Cat Sales], DESC)
RETURN SUMX(top3, [Cat Sales])
Cleaner for measure logic; no source table argument needed.
Category Table with Margin =
ADDCOLUMNS(
SUMMARIZE('Superstore', 'Superstore'[Category]),
"Sales", [Total Sales],
"Profit", [Total Profit],
"Margin %", DIVIDE([Total Profit], [Total Sales], 0)
)
`ADDCOLUMNS` lets you compute measures per grouped row.
GROUPBY
-- GROUPBY requires iterator expressions (X functions)
Sales by Region (GROUPBY) =
GROUPBY(
'Superstore',
'Superstore'[Region],
"Sales", SUMX(CURRENTGROUP(), 'Superstore'[Sales])
)
Use when you need CURRENTGROUP() for custom aggregations.
SUMMARIZECOLUMNS
is often more efficient and readable.COALESCE
/DIVIDE
to avoid empty totals.
Use a single file to learn SUMMARIZE, SUMMARIZECOLUMNS, and set-ops (UNION, EXCEPT, INTERSECT) without the noise of a big dataset.
Dataset link:
Open the One-File Google Sheet →
"SalesOrders"
. Remove the Table column."Customers"
. Remove the Table column.Create a simple lookup so you can use Segment by Region with orders.
Now you can filter orders by the Customers table’s Region/Segment.
Total Sales = SUM('SalesOrders'[Sales])
We’ll reuse [Total Sales]
in all examples below.
Group Sales by Region & Category and materialize a small table:
Sales by Region & Category =
SUMMARIZE(
'SalesOrders',
'SalesOrders'[Region],
'SalesOrders'[Category],
"Total Sales", [Total Sales]
)
Use this table directly in a Table visual, or as a source for further modeling.
Compute Top 2 Regions by Sales under current slicers:
Top 2 Region Sales =
VAR t =
SUMMARIZECOLUMNS(
'SalesOrders'[Region],
"Reg Sales", [Total Sales]
)
VAR top2 = TOPN(2, t, [Reg Sales], DESC)
RETURN SUMX(top2, [Reg Sales])
Drop on a Card; change slicers to see it update.
Build a grouped table on Category and compute dynamic expressions per row:
Category Table =
ADDCOLUMNS(
SUMMARIZE('SalesOrders', 'SalesOrders'[Category]),
"Sales", [Total Sales]
)
Use this as a source for a simple Table visual to compare Category totals.
Regions with Orders = VALUES('SalesOrders'[Region])
Regions with Customers = VALUES('Customers'[Region])
Common Regions =
INTERSECT( Regions with Orders, Regions with Customers )
Calculated tables → show as separate tables to explain overlaps.
New Regions =
EXCEPT( Regions with Orders, Regions with Customers )
Lost Regions =
EXCEPT( Regions with Customers, Regions with Orders )
Explains “where we sell but have no customers yet” vs the opposite.
All Regions (Union) =
UNION( Regions with Orders, Regions with Customers )
Use for quick reference lists or slicers.
[Total Sales]
.[Top 2 Region Sales]
.Figure: Venn representations of DAX set operations — INTERSECT, UNION, EXCEPT.
Regions with Orders = VALUES('SalesOrders'[Region])
Regions with Customers = VALUES('Customers'[Region])
Common Regions =
INTERSECT(
Regions with Orders,
Regions with Customers
)
Gives you only the regions present in both tables.
New Regions =
EXCEPT(
Regions with Orders,
Regions with Customers
)
Lost Regions =
EXCEPT(
Regions with Customers,
Regions with Orders
)
Find regions with orders but no customers, and vice versa.
All Regions (Union) =
UNION(
Regions with Orders,
Regions with Customers
)
Creates a full list of unique regions from both tables.
DISTINCT() returns a one-column table with unique values from a column, perfect for creating slicers, validating duplicates, or calculating distinct counts. Dataset: Superstore Google Sheet →
Unique Regions =
DISTINCT('Superstore'[Region])
This calculated table lists each Region only once (East, West, Central, South). Useful for disconnected slicers or validation.
Distinct Customers =
COUNTROWS(
DISTINCT('Superstore'[Customer Name])
)
Counts the unique customers in the dataset. Similar to DISTINCTCOUNT()
, but more flexible in advanced scenarios.
Distinct Categories =
COUNTROWS(
DISTINCT('Superstore'[Category])
)
Returns how many unique Categories exist in Superstore (Furniture, Technology, Office Supplies).
DISTINCT
for raw uniqueness; use VALUES
when working with slicers and current context.Regions (Values) =
VALUES('Superstore'[Region])
Try placing both DISTINCT and VALUES in a Table visual with slicers to see the difference in behavior.
Unique Regions
table → shows 4 rows.[Distinct Customers]
.[Distinct Categories]
.DISTINCT
vs VALUES
.
Both functions **modify filter context**. The key difference: CALCULATE returns a scalar (number), while CALCULATETABLE returns a table you can iterate (e.g., with SUMX
, COUNTROWS
).
Dataset: Superstore (see DISTINCT section) • See also: CALCULATE basics, SUMMARIZE, KEEPFILTERS, REMOVEFILTERS.
CALCULATE(<expression>, <filters...>)
Use for KPIs, cards, line/bar values.
CALCULATETABLE(<table expression>, <filters...>)
Use for virtual tables → iterate with SUMX/TOPN/COUNTROWS
or materialize as calculated tables.
Total Sales = SUM('Superstore'[Sales])
Total Profit = SUM('Superstore'[Profit])
Furniture Sales =
CALCULATE(
[Total Sales],
'Superstore'[Category] = "Furniture"
)
East • Tech Profit =
CALCULATE(
[Total Profit],
'Superstore'[Region] = "East",
'Superstore'[Category] = "Technology"
)
Returns a single value each → perfect for Cards/Tooltips.
-- 1) Orders table for Furniture only (virtual)
Furniture Orders (table) =
CALCULATETABLE(
'Superstore',
'Superstore'[Category] = "Furniture"
)
-- 2) Iterate over that table
Furniture Order Count =
COUNTROWS( Furniture Orders (table) )
Furniture Sales via SUMX =
SUMX( Furniture Orders (table), 'Superstore'[Sales] )
Use the table result in COUNTROWS
/SUMX
to build advanced measures.
Top 3 Products by Sales =
VAR t =
CALCULATETABLE(
SUMMARIZE('Superstore', 'Superstore'[Product Name],
"Prod Sales", [Total Sales])
)
VAR top3 = TOPN(3, t, [Prod Sales], DESC)
RETURN top3 -- return a table (use in a calculated table)
Materialize as a calculated table, or wrap with CONCATENATEX
to show as a comma list.
Selected Rows (table) =
CALCULATETABLE('Superstore') -- current context table
All Rows (table) =
CALCULATETABLE('Superstore', REMOVEFILTERS('Superstore'))
% Selected vs All =
DIVIDE(
SUMX(Selected Rows (table), 'Superstore'[Sales]),
SUMX(All Rows (table), 'Superstore'[Sales]),
0
)
Great for % of total that **respects** current slicers (top) vs **ignores** (bottom).
CALCULATE
. If you need a set of rows → use CALCULATETABLE
.Covers: CALCULATE, CALCULATE vs CALCULATETABLE, SUMMARIZE, DISTINCT vs VALUES, KEEPFILTERS, REMOVEFILTERS, Time Intelligence, RELATED/LOOKUPVALUE, and set operations.
Quick answers with deep dives linked to relevant sections of this guide.
SUMX/COUNTROWS
. Examples: Mastering DAX: CALCULATE vs CALCULATETABLE.REMOVEFILTERS
to clear context for the denominator:Grand Total = CALCULATE([Total Sales], REMOVEFILTERS('Superstore'))
→ % of GT = DIVIDE([Total Sales], [Grand Total])
. Learn more: REMOVEFILTERS.SUMMARIZE
/SUMMARIZECOLUMNS
to create virtual groups, then TOPN
or RANKX
. Step-by-step: SUMMARIZE tutorial.DIVIDE(numerator, denominator, 0)
to avoid divide-by-zero. Try it in the Quick Start.Keep exploring Power BI and DAX with these hand-picked guides from Vista Academy.
Excel → SQL → Power BI → Python. Portfolio projects and placement support.
Statistics, Python, ML, model deployment. Learn by building real projects.
Solve business problems with BI dashboards, data storytelling, and KPIs.
Hands-on ML bootcamp: supervised/unsupervised models, evaluation, deployment.