Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

What is the difference between RANK and DENSE_RANK in SQLite?

Quick answer

In SQLite, RANK() gives tied rows the same rank and leaves a gap before the next rank. DENSE_RANK() also gives tied rows the same rank, but continues with the next consecutive number. Use PARTITION BY to restart either ranking within each group. The window’s ORDER BY determines which values tie; a separate final ORDER BY can make displayed rows easier to read. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I need to rank scores separately for each team. What is the practical difference between RANK() and DENSE_RANK() when scores tie?

Geez

Both functions assign the same number to rows that tie under the window’s ORDER BY. The difference is what comes after the tie: RANK() leaves a gap, while DENSE_RANK() does not. For example, if two rows share the highest score, those rows get rank 1 with either function. The next lower score gets rank 3 with RANK(), but rank 2 with DENSE_RANK(). In SQLite, use PARTITION BY to restart the rankings for each team. [S1]

The distinction matters when the number is meant to represent a position in a list versus a consecutive tier. A competition-style placement can have gaps; a label for consecutive score bands usually should not. These functions rank peer groups, so tied rows share a rank rather than being arbitrarily separated.

Follow-up question

Could you show both rankings in one query, with an invented example that makes the gap visible?

Geez

Assume a table named results with team, player, and numeric score columns. This query returns both rankings for each team:

SELECT
  team,
  player,
  score,
  RANK() OVER (
    PARTITION BY team
    ORDER BY score DESC
  ) AS rank_position,
  DENSE_RANK() OVER (
    PARTITION BY team
    ORDER BY score DESC
  ) AS dense_position
FROM results
ORDER BY team, score DESC, player;

For an invented input, suppose Team A has Mira 90, Niko 90, and Oren 80; Team B has Paz 75 and Quin 60. The result would be:

team  player  score  rank_position  dense_position
A     Mira    90     1              1
A     Niko    90     1              1
A     Oren    80     3              2
B     Paz     75     1              1
B     Quin    60     2              2

Team A’s tie creates the visible gap in rank_position; Team B starts over because of PARTITION BY team. The final ORDER BY controls display order. Its player column does not change which rows tie in the window ranking, because the window’s own ordering uses only score. [S1]

Follow-up question

If I add the player name to the window’s ORDER BY to make the output stable, will the tied scores still share a rank?

Geez

Not if the names differ. Rows are peers when all values in the window’s ORDER BY match. So ORDER BY score DESC, player makes different player names separate ordering values even when their scores match; they would no longer be tied for ranking purposes. [S1]

If equal scores should share a rank, keep the window ordering as ORDER BY score DESC. Then use a separate query-level ordering, such as ORDER BY team, score DESC, player, to choose a readable order among tied rows. The displayed sequence can be consistent without changing the ranking rule.

Follow-up question

How should I choose between them, and what should I check before using this in my own query?

Geez

Choose RANK() when later positions should reflect the number of rows ahead, including the tied rows. Choose DENSE_RANK() when you want consecutive rank numbers for distinct score levels. Before adapting the example, confirm the grouping column, the score direction, and which columns define a tie. The example assumes each team’s ranking is based only on score; a different business rule may call for different window-ordering columns.

The ranking behavior shown here does not determine which meaning your application should use or establish anything about your particular table and data. Check the results against a small sample containing a tie and a second group before relying on the output.

A practical checklist

  • Choose RANK() for gaps after ties or DENSE_RANK() for consecutive score-level numbers.

  • Put the group column in PARTITION BY if each group should restart at rank 1.

  • Use only the intended tie-defining columns in the window’s ORDER BY.

  • Use the query-level ORDER BY for readable row display without changing ties.

  • Check the query with a sample that includes tied values and more than one group.

A few more useful answers

Does `PARTITION BY` restart both functions?

Yes. Each partition is ranked independently, so the rank numbering starts over for each group. [S1]

Can I use both functions in one SQLite query?

Yes. As in the example, select both window-function expressions, giving each a different alias.

Does the final `ORDER BY` determine whether rows tie?

No. The window’s ORDER BY defines peers for ranking; the final query ORDER BY determines the displayed row order. [S1]

Sources

References used for this guide.

  1. [S1] SQLite Window Functions: rank() and dense_rank()
    sqlite.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work