SUM, SUMX, COUNTROWS, DISTINCTCOUNT & RELATED
Most business measures start with adding or counting something: total sales, number of bills, number of different customers. DAX has two families of functions for this. Aggregators such as SUM work on one column. Iterators such as SUMX go through a table row by row, calculate an expression for each row, and then add up the results. Knowing when you need an iterator is a big step in learning DAX.
In this lesson you will build Kesar Mart's sales, cost and profit measures, count rows and distinct values, and use RELATED to fetch a product's cost price while iterating over sales.
Aggregators: one column at a time
An aggregator takes a single column and returns one number for the current filter context:
| Function | Returns |
|---|---|
SUM ( Sales[Amount] ) | Total of the column |
AVERAGE ( Sales[Amount] ) | Simple average of the column |
MIN ( Sales[Amount] ) / MAX ( Sales[Amount] ) | Smallest / largest value |
COUNTROWS ( Sales ) | Number of rows in a table |
DISTINCTCOUNT ( Sales[StoreID] ) | Number of different values in a column |
For all 16 Kesar Mart lines, AVERAGE ( Sales[Amount] ) is 2,454.50, MAX is 3,900 (order 1010) and MIN is 1,299 (order 1007).
Prefer COUNTROWS ( Sales ) for "how many lines" over counting a column: it says exactly what you mean and does not depend on whether a column has blanks.
The error that teaches you SUMX
Suppose Sales had no Amount column, only Qty and UnitPrice. A beginner writes this measure:
Revenue = Sales[Qty] * Sales[UnitPrice]Power BI rejects it:
A single value for column 'Qty' in table 'Sales' cannot be determined. This can happen when a measure formula refers to a column that contains many values without specifying an aggregation such as min, max, count, or sum to get a single result.
A measure has no "current row"; it sees a whole set of rows. And multiplying two totals, SUM ( Sales[Qty] ) * SUM ( Sales[UnitPrice] ), would be wrong too (76 × 11,311 is not revenue). You need to multiply per row, then add. That is what an iterator does:
Revenue = SUMX ( Sales, Sales[Qty] * Sales[UnitPrice] )SUMX takes a table (Sales, already filtered by the visual) and an expression. It creates a row context for each row, calculates Qty × UnitPrice, and sums the results: 39,272, the same as SUM ( Sales[Amount] ). Other iterators work the same way: AVERAGEX, MINX, MAXX, COUNTX.
RELATED: looking up the one side
Profit needs the cost price, which lives in Products, not in Sales. Inside an iterator over Sales, RELATED follows the many-to-one relationship and returns the value from the matching product row:
Total Cost = SUMX ( Sales, Sales[Qty] * RELATED ( Products[CostPrice] ) )
Total Sales = SUM ( Sales[Amount] )
Total Profit = [Total Sales] - [Total Cost]
Margin % = DIVIDE ( [Total Profit], [Total Sales] )Format Margin % as a percentage in Measure tools. By category:
| Category | Total Sales | Total Cost | Total Profit | Margin % |
|---|---|---|---|---|
| Grocery | 18,900 | 15,780 | 3,120 | 16.51% |
| Home | 20,372 | 14,340 | 6,032 | 29.61% |
| Total | 39,272 | 30,120 | 9,152 | 23.30% |
Home products earn almost twice the margin of groceries. Notice the total margin is 23.30%, not the average of 16.51% and 29.61%: the measure recalculates 9,152 / 39,272.
RELATED needs two things: a row context (inside an iterator or a calculated column) and a relationship from the current table to the target table on the one side. If either is missing you see an error saying the column "either doesn't exist or doesn't have a relationship to any table available in the current context".
RELATEDTABLE: looking down to the many side
The opposite direction uses RELATEDTABLE, which returns the rows of the many side that belong to the current row. As a calculated column in Products:
Lines Sold = COUNTROWS ( RELATEDTABLE ( Sales ) )Product | Lines Sold --------------------+----------- Basmati Rice 5 kg | 4 Masala Chai 500 g | 3 Steel Water Bottle | 3 Pressure Cooker 3 L | 4 Cotton Bedsheet | 2
Because it is a calculated column, it is fixed at refresh. For a number that reacts to slicers, use a measure such as COUNTROWS ( Sales ) with Product in the visual instead.
DISTINCTCOUNT: how many different?
"How many stores sell this product?" is a distinct count:
Stores Selling = DISTINCTCOUNT ( Sales[StoreID] )| Product | Lines Sold | Stores Selling |
|---|---|---|
| Basmati Rice 5 kg | 4 | 4 |
| Pressure Cooker 3 L | 4 | 3 |
| Masala Chai 500 g | 3 | 3 |
| Steel Water Bottle | 3 | 3 |
| Cotton Bedsheet | 2 | 2 |
The pressure cooker has four lines but only three stores, because Pune sold it twice. COUNTROWS and DISTINCTCOUNT answer different questions; choose deliberately.
AVERAGEX over a dimension
"Average sales per store" is not the average of sales lines. Iterate over the stores instead:
Avg Sales per Store = AVERAGEX ( VALUES ( Stores[City] ), [Total Sales] )VALUES ( Stores[City] ) returns the list of cities visible in the current filter context. For each city, AVERAGEX evaluates [Total Sales] (a measure inside an iterator is calculated for that one city) and then averages: (10,475 + 7,862 + 9,594 + 11,341) / 4 = 9,818. Filter the report to the North region and it becomes the average of Jaipur and Lucknow.
Common mistakes
- Multiplying two sums.
SUM ( Qty ) * SUM ( UnitPrice )is not revenue. UseSUMXfor row-by-row maths. - Using
RELATEDoutside a row context. In a plain measure it fails. Wrap it in an iterator such asSUMX. - Using
RELATEDin the wrong direction. FromProductsyou cannotRELATEDa sales column; useRELATEDTABLE. - Counting a column when you mean rows.
COUNT ( Sales[OrderID] )skips blanks;COUNTROWS ( Sales )counts every line. - Averaging averages. The average of four city margins is not the company margin. Divide totals instead.
Key points
- Aggregators (
SUM,AVERAGE,MIN,MAX,DISTINCTCOUNT) work on one column;COUNTROWScounts table rows. - Iterators (
SUMX,AVERAGEX,MAXX...) evaluate an expression for each row of a table, then aggregate. RELATEDfetches a value from the one side of a relationship inside a row context;RELATEDTABLEreturns the related rows on the many side.- Build complex measures from simple ones: Total Cost, Total Profit, then Margin %.
- Ratios of totals, not averages of ratios, give correct totals.
Exercise
Kesar Mart's owner asks for two new measures: (1) Avg Line Value, the average amount per sales line, and (2) Best Line, the largest single sales line amount. Then write (3) Cost Share %: total cost as a percentage of total sales. What do they show for the Home category?
Show solution
Avg Line Value = AVERAGE ( Sales[Amount] )
Best Line = MAX ( Sales[Amount] )
Cost Share % = DIVIDE ( [Total Cost], [Total Sales] )Home has 9 lines worth 20,372 in total, so Avg Line Value is about 2,263.56. The largest Home line is order 1014 (3 pressure cookers in Pune), so Best Line is 3,897. Cost Share % is 14,340 / 20,372 = 70.39%, which is simply 100% minus Home's 29.61% margin, a handy cross-check.
Quick Quiz
1. Why does Revenue = Sales[Qty] * Sales[UnitPrice] fail as a measure?
Columns in a measure must be aggregated or used inside an iterator such as SUMX.
2. What does SUMX ( Sales, Sales[Qty] * Sales[UnitPrice] ) do?
SUMX iterates the table, evaluating the expression per row before summing.
3. When can you use RELATED ( Products[CostPrice] )?
RELATED needs a row context and follows the relationship to the one side.
4. The pressure cooker has 4 sales lines. DISTINCTCOUNT ( Sales[StoreID] ) shows 3. Why?
DISTINCTCOUNT counts different values, so two Pune lines count once.
Learn Power BI with a mentor
Free lessons get you started. Our Data Analytics & Power BI training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
