# How to use ORDER BY with window functions

TIP

Check out the interactive database fiddle[1] to play around with the code covered in this chapter.

Let's say we want to rank the polls in order of number of votes.

So if we had 3 polls like this:

title vote_count
Flask vs. Django 5
Python vs. Java 10
Windows vs. Mac 2

Then we want to give each a rank according to their vote count:

title vote_count rank
Flask vs. Django 5 2
Python vs. Java 10 1
Windows vs. Mac 2 3

We can do this using another window function called RANK(). First though, we need to get the polls and their vote count across all the options. We can do that without a window function:

SELECT polls.title, COUNT(votes) as vote_count
FROM polls
LEFT JOIN options ON options.poll_id = polls.id
LEFT JOIN votes ON votes.option_id = options.id
GROUP BY polls.id;

This gives us the first table, with title and vote count:

title vote_count
Flask vs. Django 5
Python vs. Java 10
Windows vs. Mac 2

Now we can use the RANK() window function to go through the results and rank them in order:

SELECT
  polls.title,
  COUNT(votes) as vote_count,
  RANK() OVER()
FROM polls
LEFT JOIN options ON options.poll_id = polls.id
LEFT JOIN votes ON votes.option_id = options.id
GROUP BY options.id;

But we haven't told RANK() OVER() what it should rank by. Is it title? vote_count? Something else?

Since it doesn't know, every row gets the same rank (there's nothing to rank by). The result is this:

title vote_count rank
Flask vs. Django 5 1
Python vs. Java 10 1
Windows vs. Mac 2 1

Not exactly what we want!

Instead, we must give RANK() OVER() a column to sort by. The way to do this is by providing OVER() with a sorting column:

SELECT
  polls.title,
  COUNT(votes) as vote_count,
  RANK() OVER(ORDER BY COUNT(votes) DESC)
FROM polls
LEFT JOIN options ON options.poll_id = polls.id
LEFT JOIN votes ON votes.option_id = options.id
GROUP BY options.id;

Now it's more like it:

title vote_count rank
Flask vs. Django 5 2
Python vs. Java 10 1
Windows vs. Mac 2 3

Note that the ORDER BY inside the OVER() and the ORDER BY that we can have on the outer query can be different. This is totally valid:

SELECT
  polls.title,
  COUNT(votes) as vote_count,
  RANK() OVER(ORDER BY COUNT(votes) DESC)
FROM polls
LEFT JOIN options ON options.poll_id = polls.id
LEFT JOIN votes ON votes.option_id = options.id
GROUP BY options.id
ORDER BY vote_count ASC;

And the result:

title vote_count rank
Windows vs. Mac 2 3
Flask vs. Django 5 2
Python vs. Java 10 1

  1. Interactive database fiddle for this chapter (opens new window) ↩ī¸Ž