RANK vs DENSE_RANK: Keep Poll Ties at the Cutoff

RANK vs DENSE_RANK changes which tied poll options pass a cutoff. First decide whether you want competition positions, distinct score levels or a fixed number of rows. These rules can produce different answers from the same votes.

Matching red flower pots at tied heights and a gap in one ranking rack.

Use invented votes to make the tie rule visible

The main poll has vote totals 100, 80, 80, 60 and 40. These values are made up. The table also holds a second poll where every option has zero votes and a third poll with a tie at the cutoff. This works on SQL Server 2012 or later.

DROP TABLE IF EXISTS #PollOptions;
CREATE TABLE #PollOptions(PollId int NOT NULL,OptionId int NOT NULL,
  OptionLabel nvarchar(20) NOT NULL,Votes int NOT NULL,PRIMARY KEY(PollId,OptionId));
INSERT #PollOptions VALUES
  (1,1,N'First',100),(1,2,N'Second',80),(1,3,N'Third',80),(1,4,N'Fourth',60),(1,5,N'Fifth',40),
  (2,1,N'Zero A',0),(2,2,N'Zero B',0),(2,3,N'Zero C',0),
  (3,1,N'Cutoff A',100),(3,2,N'Cutoff B',80),(3,3,N'Cutoff C',60),(3,4,N'Cutoff D',60);
SELECT PollId,OptionId,OptionLabel,Votes,
  ROW_NUMBER() OVER(PARTITION BY PollId ORDER BY Votes DESC,OptionId) AS RowSequence,
  RANK() OVER(PARTITION BY PollId ORDER BY Votes DESC) AS CompetitionRank,
  DENSE_RANK() OVER(PARTITION BY PollId ORDER BY Votes DESC) AS DensePosition,
  RANK() OVER(PARTITION BY PollId ORDER BY Votes DESC,OptionId) AS TiesBrokenById
 INTO #PollRanks FROM #PollOptions;

The setup block above creates and fills #PollOptions. PARTITION BY PollId keeps each poll separate. Votes define ranking ties. The final display can use OptionId to order tied options without changing their shared rank.

SSMS Light displays five poll rows, competition ranks 1, 2, 2, 4, 5 and dense ranks 1, 2, 2, 3, 4.
These five poll rows show how the tie at rank 2 creates a gap in RANK, while DENSE_RANK advances without that gap.

Choose the cutoff that matches the question

RANK gives the main poll positions 1, 2, 2, 4 and 5. DENSE_RANK gives levels 1, 2, 2, 3 and 4. Both preserve the equal 80-vote options. Their difference is the gap after that tie.

Filtering CompetitionRank <= 3 returns three options here. Filtering DensePosition <= 3 returns four options. The latter includes the 60-vote option because it has the third distinct score. Neither rule promises exactly three rows for every poll.

SELECT PollId,
  SUM(CASE WHEN CompetitionRank<=3 THEN 1 ELSE 0 END) AS RankCutoffRows,
  SUM(CASE WHEN DensePosition<=3 THEN 1 ELSE 0 END) AS DenseCutoffRows,
  SUM(CASE WHEN RowSequence<=3 THEN 1 ELSE 0 END) AS RowLimitRows
FROM #PollRanks GROUP BY PollId ORDER BY PollId;

The third poll has totals 100, 80, 60 and 60. Its competition ranks are 1, 2, 3 and 3. A rank-three cutoff keeps both tied 60-vote options. Therefore, that cutoff returns four rows, even though its number is three.

RANK versus DENSE_RANK

Keep row selection separate from equality

ROW_NUMBER assigns a separate sequence to each row. This example orders by votes and the unique option identifier within each poll. Filtering the sequence can enforce a three-row limit. That selection intentionally chooses among tied scores.

Adding OptionId to the ranking window also changes the tie definition. With unique option identifiers, equal vote totals no longer tie across every ordering expression. The query below shows TiesBrokenById matching RowSequence on every row. Use the extra identifier only where that behavior is intended.

SELECT PollId,OptionId,Votes,RowSequence,CompetitionRank,DensePosition,TiesBrokenById
FROM #PollRanks ORDER BY PollId,Votes DESC,OptionId;

TOP WITH TIES follows the whole ordering list

DECLARE @WithTies bigint,@WithUniqueId bigint;
SELECT @WithTies=COUNT_BIG(*) FROM
  (SELECT TOP(3) WITH TIES OptionId FROM #PollOptions WHERE PollId=3 ORDER BY Votes DESC) AS t;
 SELECT @WithUniqueId=COUNT_BIG(*) FROM
  (SELECT TOP(3) WITH TIES OptionId FROM #PollOptions WHERE PollId=3 ORDER BY Votes DESC,OptionId) AS t;
SELECT @WithTies AS TopWithVoteTies,@WithUniqueId AS TopWithUniqueId;

On the cutoff-tie poll, ordering only by votes returns four rows. Adding the unique option identifier returns three. TOP WITH TIES compares the complete ORDER BY criteria at the boundary.

Check zero votes and missing polls explicitly

All three zero-vote options in the second poll share rank one and dense position one. That does not turn them into meaningful winners. A poll without options produces no ranked rows. Decide whether your user interface should show ties, no participation or an empty poll.

When you are done, drop the demo tables.

DROP TABLE #PollRanks;
DROP TABLE #PollOptions;

Try the demo table with your own poll numbers and see where the cutoff lands.

A rank is not a row count, it is a position that ties can share.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

Ranking Functions, SQL Function, SQL Order By
Previous Post
SQL SERVER – FIX : Error: Msg 15123, Level 16 – The configuration option ‘advance option’ does not exist, or it may be an advanced option.
Next Post
SQL SERVER – Fix : Error : Msg 2714, Level 16, State 6 – There is already an object named ‘#temp’ in the database

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.