问题
Similar to this question, I have the following table in MySQL 8.0.15:
CREATE TABLE golf_scores (id INT PRIMARY KEY AUTO_INCREMENT, person TEXT, score INT, age INT);
INSERT INTO golf_scores (person, score, age) VALUES ('Angela', 40, 25),('Angela', 45, 25),('Angela', 55, 25),('Peter',45, 32),('Peter',55,32),('Rachel', 65, 35),('Rachel',75,35),('Jeff',75, 16);
SELECT * FROM golf_scores;
+----+--------+-------+------+
| id | person | score | age |
+----+--------+-------+------+
| 1 | Angela | 40 | 25 |
| 2 | Angela | 45 | 25 |
| 3 | Angela | 55 | 25 |
| 4 | Peter | 45 | 32 |
| 5 | Peter | 55 | 32 |
| 6 | Rachel | 65 | 35 |
| 7 | Rachel | 75 | 35 |
| 8 | Jeff | 75 | 16 |
+----+--------+-------+------+
We want to select the following "best" 3 rows:
+----+--------+-------+------+
| id | person | score | age |
+----+--------+-------+------+
| 1 | Angela | 40 | 25 |
| 4 | Peter | 45 | 32 |
| 6 | Rachel | 65 | 35 |
+----+--------+-------+------+
In other words, the lowest 3 golf scores without having duplicates by person, and also the other columns from that row. I'm not worried about ties; I'd still just like three results.
The query SELECT person, MIN(score) as min_score FROM golf_scores GROUP BY person ORDER BY min_score LIMIT 3;
gives the right rows, but is limited to the columns person
and score`. When I try to modify it like this:
SELECT id, person, MIN(score) as min_score, age FROM golf_scores GROUP BY person ORDER BY min_score LIMIT 3;
I get this error:
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'records.golf_scores.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
I also tried eliminating duplicate names with SELECT id, DISTINCT person, score, age FROM golf_scores ORDER BY score LIMIT 3
but I get an error
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT person, score FROM golf_scores ORDER BY score LIMIT 3' at line 1
How can I get the desired output in MySQL?
回答1:
Use row_number()
:
select t.*
from (select t.*, row_number() over (partition by person order by score) as seqnum
from golf_scores t
) t
where seqnum = 1
order by score asc
limit 3;
In older versions, you can do this by using a correlated subquery and id
:
select gs.*
from golf_scores gs
where gs.id = (select gs2.id
from golf_scores gs2
where gs2.person = gs.person
order by gs2.score asc
limit 1
)
order by score asc
limit 3;
This may also be the fastest way with an index on golf_scores(person, score, id)
.
回答2:
Here's one way:
SELECT x.*
FROM golf_scores x
JOIN
( SELECT MIN(id) id FROM
( SELECT a.*
FROM golf_scores a
JOIN
( SELECT person, MIN(score) score FROM golf_scores GROUP BY person ) b
ON b.person = a.person
AND b.score = a.score
) n
GROUP
BY person
, score
) y
ON y.id = x.id
ORDER
BY x.score LIMIT 3;
来源:https://stackoverflow.com/questions/54710377/show-top-n-rows-by-category-in-mysql-8-without-duplicates-in-another-category