DAX RANKX: Ranking with Ties & Custom Order

DAX RANKX: Ranking with Ties & Custom Order
Understand how the DAX RANKX function works in Power Pivot with practical ranking examples. This tutorial explains how to rank sales, products, employees, customers, or categories, handle ties, use ascending or descending order, and build dynamic ranking formulas based on filter context. Ideal for Excel users, analysts, finance teams, and Power Pivot learners who want to create accurate ranking calculations in DAX.

Ranking data is one of the most common tasks in Power Pivot and Power BI. RANKX is the DAX function built for it. It ranks a value within a set you define. Unlike COUNTROWS+EARLIER, RANKX works inside measures, handles ties, and gives precise control over sort direction and tie mode.

This guide covers the RANKX syntax and the TIEs and ORDER arguments. Six examples follow: sales rep ranking, tied values with DENSE and SKIP, ranking inside a category, filtered ranking with ALLSELECTED, percentage rank, and a top-N filter measure.

Syntax: RANKX(table, expression, [value], [order], [ties]). This works in both measures and calculated columns in Power Pivot (Excel 365, 2019, 2021) and Power BI Desktop.

RANKX Arguments Explained

RANKX takes up to five arguments. The first two are required. The remaining three are optional and default to safe values that cover most use cases.

ArgumentRequiredWhat it controls
tableYesThe set of rows to rank against. Typically ALL(Column) or ALLSELECTED(Column).
expressionYesThe measure evaluated for each row. Usually something like [Total Sales].
valueOptionalThe value to rank. Almost always omitted — defaults to the current context result.
orderOptionalDESC (default) = highest is rank 1. ASC = lowest is rank 1 — useful for costs or defects.
tiesOptionalSKIP (default) = shared rank, next rank skips. DENSE = consecutive ranks, no gaps.
SKIP vs DENSE ties: With SKIP, two rows tied at rank 2 push the next rank to 4 (rank 3 is skipped). With DENSE, the next rank is 3 instead. Use DENSE for leaderboards. Use SKIP for sports-style competition ranking.

Examples 1–4: RANKX in Practice

1
Rank sales reps by total revenue — descending

The simplest RANKX measure ranks the current row’s sales against all reps. ALL(Sales[SalesRep]) provides the full comparison set. The highest revenue gets rank 1 by default.

Sales Rep
Revenue
Rank
Alice
142,500
1
Bob
128,000
2
Carol
114,200
3
Sales Rank := RANKX(ALL(Sales[SalesRep]), [Total Sales])
2
Handle ties — DENSE rank for a leaderboard

When two reps have identical revenue, both receive the same rank. Specifically, the tie mode determines what happens next. DENSE produces consecutive numbers without gaps. SKIP skips rank numbers after a tie. For user-facing leaderboards, DENSE is therefore the better choice.

Rep
Revenue
SKIP
DENSE
Alice
142,500
1
1
Bob
128,000
2
2
Carol
128,000
2
2
Dave
114,200
4
3
Rank SKIP := RANKX(ALL(Sales[SalesRep]), [Total Sales],, DESC, Skip) Rank DENSE := RANKX(ALL(Sales[SalesRep]), [Total Sales],, DESC, Dense)
3
Rank within category — per-region ranking per product

To rank within a category, use ALLEXCEPT in the table argument. This tells RANKX to compare only against rows in the same category. For example, regions rank separately within each product group. The result is a separate rank 1 for each product’s top region.

Rank in Product := RANKX(ALLEXCEPT(Sales, Sales[Product]), [Total Sales]) North within Widget A: ranks 1 to N (Widget A rows only). North within Widget B: a fresh rank 1 to N (Widget B rows only).
4
Rank in filtered view — ALLSELECTED respects slicers

ALLSELECTED makes RANKX rank only against currently visible rows. Additionally, the rank updates automatically as the user changes filters. This is therefore the right approach for interactive reports — rank 1 is always the top visible performer.

Visible Rank := RANKX(ALLSELECTED(Sales[SalesRep]), [Total Sales]) Slicer showing 5 of 20 reps: ranks 1 to 5 only.

Examples 5–6: Percentage Rank and Top-N Filter

5
Percentage rank — percentile position in the ranking

A percentage rank expresses position as a fraction of the total. Rank 1 of 20 is the 95th percentile. RANKX combined with COUNTROWS produces this measure without a helper column. It is especially useful in HR and performance reporting contexts.

Percentile Rank := VAR N = COUNTROWS(ALL(Sales[SalesRep])) VAR Rank = RANKX(ALL(Sales[SalesRep]), [Total Sales]) RETURN DIVIDE(N - Rank + 1, N) Rank 1/20: 100%. Rank 10/20: 55%. Rank 20/20: 5%.
6
Top-N filter — show only rank 1 through 5 in PivotTable

A True/False measure based on RANKX filters the PivotTable to the top N items. Add the rank measure, then use Value Filters in the PivotTable to show only rows where the flag is TRUE. The result is a dynamic top-5 list that updates on every refresh.

Is Top 5 := RANKX(ALL(Sales[SalesRep]), [Total Sales]) <= 5 PivotTable: Value Filters > Is Top 5 > equals > TRUE.

Common Issues and How to Fix Them

RANKX returns the same rank for every row

The expression argument is likely returning the same value in every row. Verify that your measure changes as RANKX iterates each row in the ranking table. If the measure uses ALL internally and returns a constant, every row gets rank 1. Remove any ALL calls from inside the expression argument.

RANKX returns blank instead of a rank number

RANKX returns blank when the expression evaluates to blank or zero for the current context. A product-region combination with no transactions is a common cause. Also check whether your ALLSELECTED table includes the current item. Use IFERROR or IF(ISBLANK(...)) to provide a fallback value.

Frequently Asked Questions

  • How does RANKX handle tied values?+
    RANKX handles ties through its fifth argument. SKIP (the default) assigns the same rank to tied rows and skips the following rank numbers. For example, two rows tied at rank 2 both get rank 2, and the next row gets rank 4. DENSE assigns the same rank but uses consecutive numbers, so the next row after the tie gets rank 3. Use Dense for leaderboards and Skip for sports-style competition ranking.
  • What is the difference between RANKX with ALL and ALLSELECTED?+
    ALL ranks against the full table regardless of slicers. The rank therefore remains stable as the user filters. ALLSELECTED ranks only against rows visible after slicers are applied. The rank adjusts dynamically. Use ALL for absolute ranking across the full dataset. Use ALLSELECTED for relative ranking within the user’s current filtered view.
  • Can RANKX rank in ascending order?+
    Yes, this is straightforward. Pass ASC as the fourth argument. The default is DESC (highest = rank 1). ASC makes the lowest value rank 1 — useful for defect rates, response times, or costs where lower is better. For example, RANKX(ALL(Products[Name]), [DefectRate],, ASC) ranks the product with the fewest defects as rank 1.
  • Can I use RANKX in a calculated column?+
    Yes. In fact, RANKX works in both measures and calculated columns. In a calculated column, omit the third argument — DAX uses the current row’s expression result automatically. A calculated column stores a persistent rank value. A measure recalculates dynamically based on the current filter context.