MySQL's alternative to T-SQL's WITH TIES
I have a table from which I want to get the top N records. The records are ordered by values and some records have the same values. What I'd like to do here is to get a list of top N records, including the tied ones. This is what's in the table:
+-------+--------+
| Name | Value |
+-------+--------+
| A | 10 |
| B | 30 |
| C | 40 |
| D | 40 |
| E | 20 |
| F | 50 |
+-------+--------+
Now if I want to get the top 3 like so
SELECT * FROM table ORDER BY Value DESC LIMIT 3
I get this:
+-------+--------+
| Name | Value |
+-------+--------+
| F | 50 |
| C | 40 |
| D | 40 |
+-------+--------+
What I would like to get is this
+-------+--------+
| Name | Value |
+-------+--------+
| F | 50 |
| C | 40 |
| D | 40 |
| B | 30 |
+-------+--------+
I calculate the rank of each record so what I would really like is to get the first N ranked records instead of the first N records ordered by value. This is how I calculate the rank:
SELECT Value AS Val, (SELECT COUNT(DISTINCT(Value))+1 FROM table WHERE Value > Val) as Rank
In T-SQL something like this is achievable by doing this:
SELECT TOP 3 FROM table ORDER BY Value WITH TIES
Does anyone have an idea how to do this in MySQL? I understand it could be done with subqueries or temporary tables but I don't have enough knowledge to accomplish this. I'd prefer a solution without using temporary tables.
Does this work for you?
select Name, Value from table where Value in (
select distinct Value from table order by Value desc limit 3
) order by Value desc
Or perhaps:
select a.Name, a.Value
from table a
join (select distinct Value from table order by Value desc limit 3) b
on a.Value = b.Value
select a.Name, a.Value
from table a
join (select Value from table order by Value desc limit 3) b
on a.Value = b.Value
This is like @Fosco's answer, but without DISTINCT
in the subquery. His version returns the players with the top N scores, not the top N players (plus ties). E.g. if the scores are 50, 50, 50, 40, 40, 30, 20, he'll return 6 players (3x50, 2x40, 1x30), but you presumably just want 3x50.
Starting with MySQL 8, you can use window functions to emulate the WITH TIES
semantics, by filtering on RANK()
. For example:
SELECT Name, Value
FROM (
SELECT Name, Value, RANK() OVER (ORDER BY Value DESC) AS rk
FROM table
) t
WHERE rk <= 3
Note that when reading your question more closely, this doesn't do exactly what you seem to want, but it does exactly what T-SQL can do through the TOP n WITH TIES
clause.