DAX SUMX & AVERAGEX: Iterative X‑Functions Explained

DAX SUMX & AVERAGEX: Iterative X‑Functions Explained
Understand how DAX SUMX and AVERAGEX work in Power Pivot with simple, practical examples. This tutorial explains iterator functions, row-by-row calculations, calculated totals, weighted averages, and when to use SUMX or AVERAGEX instead of regular SUM or AVERAGE. Ideal for Excel users, analysts, finance teams, and Power Pivot learners who want to build more accurate and flexible DAX measures.

Most DAX aggregation functions — SUM, AVERAGE, COUNT — operate on a single column. They take every value in that column and aggregate it. SUMX and AVERAGEX work differently. They iterate row by row through a table, evaluate an expression for each row, and then aggregate those results. This distinction matters enormously when the expression involves a calculation — such as multiplying two columns together — rather than simply reading one column.

This guide explains the X-function pattern, why it produces different results than simple column aggregation, and six practical examples. Topics include revenue from quantity times price, margin per customer, weighted average price, row-level conditional aggregation, iterating a related table, and a filtered SUMX for conditional revenue.

The X-function family: SUMX, AVERAGEX, MINX, MAXX, COUNTX, and RANKX all share the same pattern: iterate a table, evaluate an expression per row, aggregate. Understanding SUMX well means all X-functions work the same way.

Why SUMX Exists — The Row-by-Row Difference

Consider a sales table with Quantity and Price columns. You want total revenue. SUM(Sales[Quantity]) * SUM(Sales[Price]) multiplies the total quantity by the total price — but that is mathematically wrong if different products have different prices. Instead, SUMX evaluates Quantity * Price for each individual row, then sums those row-level results. This is the correct approach.

ApproachFormulaWhat it calculatesCorrect for
Wrong approachSUM(Quantity) * SUM(Price)Total qty times total price (incorrect for mixed prices)Only when all rows have identical price
SUMX (correct)SUMX(Sales, [Quantity] * [Price])Sum of (qty * price) per rowAny multi-price dataset
When SUM is faster than SUMX: If the column you want to sum already exists in the table (e.g. a Revenue column), use SUM(Sales[Revenue]). SUMX is only necessary when the aggregated value requires row-level calculation. SUM on a single column is significantly faster than SUMX on large tables.

SUMX and AVERAGEX Syntax

SUMX(table, expression) AVERAGEX(table, expression) table -- the table to iterate (can be a direct table, FILTER result, or related table) expression -- the formula evaluated for each row (uses the current row's column values)

Examples 1–4: SUMX and AVERAGEX in Practice

1
Total revenue — quantity times price per row

This is the canonical SUMX example. Each row has a Quantity and a Price. Revenue per row is Quantity * Price. SUMX sums these row-level products to produce the correct total revenue. A plain SUM approach would produce an incorrect result whenever rows have different prices.

Product
Qty
Price
Row Revenue
Widget A
10
25.00
250.00
Widget B
5
80.00
400.00
Widget C
20
12.50
250.00
Total Revenue := SUMX(Sales, Sales[Quantity] * Sales[Price]) Row 1: 10 * 25.00 = 250. Row 2: 5 * 80.00 = 400. Row 3: 20 * 12.50 = 250. SUMX total: 900. (Incorrect SUM approach: 35 * 117.50 = 4,112)
2
Gross margin — revenue minus cost per row, then summed

Gross margin is revenue minus cost. Calculating margin correctly requires the subtraction at the row level before summing. SUMX ensures this row-by-row approach. Specifically, it computes (Revenue − Cost) for each row, then sums those margins to produce the total gross margin.

Gross Margin := SUMX(Sales, (Sales[Quantity] * Sales[Price]) - Sales[Cost]) For each row: (Qty * Price) - Cost = row margin. SUMX then sums all row margins. Alternatively, if a Revenue column already exists: Gross Margin := SUMX(Sales, Sales[Revenue] - Sales[Cost])
3
Weighted average price — AVERAGEX on a row-level expression

A simple AVERAGE of a Price column gives each row equal weight. AVERAGEX can apply a weighted average instead by defining a custom expression per row. For example, averaging the unit price weighted by order size gives a revenue-weighted average selling price — a more meaningful metric for pricing analysis.

AVERAGEX averages the expression result across all rows. Each row contributes equally to the average. Simple average of unit price: Avg Unit Price := AVERAGEX(Sales, Sales[Price]) → treats each ORDER equally regardless of size Weighted average (each UNIT counted separately): Weighted Avg Price := DIVIDE( SUMX(Sales, Sales[Quantity] * Sales[Price]), SUM(Sales[Quantity]) ) → revenue-weighted: large orders influence the average more
4
Filtered SUMX — sum only rows meeting a condition

SUMX can iterate a filtered subset of rows rather than the full table. Pass a FILTER expression as the table argument. This is DAX’s equivalent of SUMIF. It is more flexible than CALCULATE+SUM because the filter condition can reference row-level calculated expressions, not just stored columns.

Sum revenue only for rows where Margin > 20%. High-Margin Revenue := SUMX( FILTER(Sales, DIVIDE(Sales[Price] - Sales[Cost], Sales[Price]) > 0.2 ), Sales[Quantity] * Sales[Price] ) FILTER returns only rows with margin > 20%. SUMX then computes revenue for only those rows. This is not possible with a simple SUMIF in Excel worksheets because SUMIF cannot filter on a calculated expression.

Examples 5–6: Related Table Iteration and Average per Category

5
Iterate a related table — SUMX across a relationship

SUMX can iterate any table — including a related table retrieved via RELATEDTABLE. This lets you sum a fact table value from the perspective of a dimension table row. For example, summing all orders linked to each customer, starting from the Customers table rather than the Orders table.

From the Customers table, sum the Revenue of all related orders. Customer Revenue := SUMX( RELATEDTABLE(Orders), Orders[Revenue] ) RELATEDTABLE(Orders) returns all Orders rows for the current customer. SUMX sums Revenue across that related subset. This produces the same result as SUM(Orders[Revenue]) when filtered by customer — but from the Customers table context.
6
Average per category — AVERAGEX over a summary table

AVERAGEX over a raw table averages individual transaction rows. Sometimes you need the average of category-level totals instead — for example, the average revenue per region. This requires first creating a summary table with one row per category, then averaging the totals. AVERAGEX with SUMMARIZE achieves this in a single measure.

Average revenue per region (not per transaction row). Avg Region Revenue := AVERAGEX( SUMMARIZE(Sales, Sales[Region]), CALCULATE([Total Sales]) ) SUMMARIZE creates one virtual row per Region. CALCULATE([Total Sales]) evaluates total revenue for each region row. AVERAGEX then averages those region-level totals. Result: average of North/South/East/West totals (not the average of individual transaction amounts)

Common Issues and How to Fix Them

SUMX returns a different result than expected

The most common cause is column reference scope. Inside SUMX, column references use the current row being iterated. If your expression accidentally references a measure that aggregates the entire table rather than the current row, the result will be wrong. Verify that the expression uses direct column references (Sales[Price]) rather than aggregation measures ([Avg Price]) inside the row expression. Specifically, use [column] syntax for row values and reserve measure references for post-aggregation calculations.

AVERAGEX ignores blank rows in the expression

AVERAGEX skips rows where the expression evaluates to blank. This is consistent with DAX averaging behaviour. However, if you expect blank rows to contribute a zero to the average, wrap the expression in IFERROR or use IF(ISBLANK(...), 0, expression) to convert blanks to zeros before averaging. This ensures blank rows are counted in the denominator.

Frequently Asked Questions

  • What is the difference between SUM and SUMX?+
    SUM adds up all values in a single column. SUMX iterates every row in a table, evaluates an expression for each row, and then sums those results. Use SUM when the value you need is already in a single column. Use SUMX when the value requires a per-row calculation first, such as Quantity times Price. SUMX is more powerful but also slower on large tables, so avoid it when SUM alone is sufficient.
  • When should I use AVERAGEX instead of AVERAGE?+
    Use AVERAGE when you want the mean of values in a single column. Use AVERAGEX when the value to average must be calculated per row. For instance, averaging the profit margin requires dividing (Price minus Cost) by Price on each row before averaging — AVERAGEX handles this correctly. A plain AVERAGE cannot compute per-row expressions. Also use AVERAGEX when you need to average totals per category rather than individual row values, by passing a SUMMARIZE table as the first argument.
  • Can SUMX and AVERAGEX use a FILTER as the table?+
    Yes. The first argument of SUMX and AVERAGEX can be any table expression, including FILTER results. SUMX(FILTER(Sales, Sales[Region] = "North"), Sales[Revenue]) sums revenue for North only. This is equivalent to CALCULATE(SUM(Sales[Revenue]), Sales[Region] = "North") in most cases, but SUMX with FILTER is more flexible when the filter condition involves a row-level calculated expression that cannot be expressed as a simple column filter.
  • Why is SUMX slower than SUM?+
    SUM uses a highly optimised column-store scan in the DAX storage engine. It does not evaluate expressions — it simply totals a pre-built column. SUMX, on the other hand, iterates row by row and evaluates an expression for each row. This row-by-row evaluation is handled by the DAX formula engine, which is significantly slower than the storage engine. On tables with millions of rows, SUMX can be noticeably slower than SUM. Always prefer SUM on a pre-calculated column when performance matters.