问题
I have a table called 'winners' with columns 'id', 'category', 'score', 'rank'.
I need to update my table and asign a rank within the subcategories (category_id) which as per the sample is 2 but could be more than that in the future.
Most anwers I've found are based around select statements which simply tends to just output the table view but I did find a very good 'Update' answer (https://stackoverflow.com/a/2727239/4560380) specifically the answer update where ties are required to share the same rank.
Sample
CREATE TABLE winners (
id int,
category_id int,
score double,
rank int
);
INSERT INTO winners VALUES
(1, 1, 4.36, NULL),
(2, 1, 2.35, NULL),
(3, 1, 1.25, NULL),
(4, 2, 4.21, NULL),
(5, 2, 3.33, NULL),
(6, 1, 4.24, NULL),
(7, 1, 1.22, NULL),
(8, 1, 1.25, NULL),
(9, 2, 4.21, NULL),
(10, 2, 3.63, NULL);
+----------+-------------+-------+------+
| id | category_id | score | rank |
+----------+-------------+-------+------+
| 1 | 1 | 4.36 | |
| 2 | 1 | 2.35 | |
| 3 | 1 | 1.25 | |
| 4 | 2 | 4.21 | |
| 5 | 2 | 3.33 | |
| 6 | 1 | 4.24 | |
| 7 | 1 | 1.22 | |
| 8 | 1 | 1.25 | |
| 9 | 2 | 4.21 | |
| 10 | 2 | 3.63 | |
+----------+-------------+-------+------+
The linked answer above works perfectly for the data when there is only one category to worry about but not when there are multiple categories or subgroups to rank within.
I had attempted to add in a where clause to the code (line 8)
1. UPDATE winners
2. JOIN (SELECT w.score,
3. IF(@lastPoint <> w.score,
4. @curRank := @curRank + 1,
5. @curRank) AS rank,
6. @lastPoint := w.rating
7. FROM winners w
8. WHERE category_id = 1
9. JOIN (SELECT @curRank := 0, @lastPoint := 0) r
10. ORDER BY w.score DESC
11. ) ranks ON (ranks.score = winners.score)
12. SET winners.rank = ranks.rank;
with the intention of attempting to run the code twice for each category_id but the script fails.
Any options on modifying the answer above for multiple categories would be fantastic.
Needed result just to clarify (ranked within categories).
+----------+-------------+-------+------+
| id | category_id | score | rank |
+----------+-------------+-------+------+
| 1 | 1 | 4.36 | 1 |
| 6 | 1 | 4.24 | 2 |
| 2 | 1 | 2.35 | 3 |
| 8 | 1 | 1.25 | 4 |
| 3 | 1 | 1.25 | 4 |
| 7 | 1 | 1.22 | 5 |
| 4 | 2 | 4.21 | 1 |
| 9 | 2 | 4.21 | 1 |
| 10 | 2 | 3.63 | 2 |
| 5 | 2 | 3.33 | 3 |
+----------+-------------+-------+------+
Thanks Guys!
UPDATE
Managed to find another bit of code https://stackoverflow.com/a/13270603/4560380 that I had somehow originally missed and was able to modifiy it with the where clause for each category_id succesfully. Not an ideal way of doing it - running multiple times for multiple categories but at this point in time it is fine.
set @currentRank = 0,
@lastRating = null,
@rowNumber = 1;
update winners r
inner join (
select
score,
@currentRank := if(@lastRating = score, @currentRank, @rowNumber) rank,
@rowNumber := @rowNumber + if(@lastRating = score, 0, 1) rowNumber,
@lastRating := score
from winners
where category_id = 1
order by score desc
) var on var.score = r.score
set r.rank = var.rank
further answers for a more 'automatic' handling of multiple categories within the ranking in 1 script run are still very welcome and appreciated.
Thanks
UPDATE
Just noticed that the answer that I found doesn't deal with zero scores (0.00) very well and places them ranked in the middle of other scores.
shawnt00 answer below is working and evaluates zero scores correctly. https://stackoverflow.com/a/34667112/4560380
回答1:
update winners
set rank = (
select count(score) + 1
from winners w2
where w2.category_id = winners.category_id and w2.score > winners.score
)
count(*)
will evaluate to zero even when no rows match the condition, i.e., the top ranking. If you wanted a dense rank you could do this:
update winners
set rank = (
select count(distinct score) + 1
from winners w2
where w2.category_id = winners.category_id and w2.score > winners.score
)
EDIT: Per your comment I've found a query that does work. (It works on SQL Server and I'm not familiar with MySQL's quirks.) http://sqlfiddle.com/#!9/1159f/1
update winners
inner join (
select w.id, w.category_id, count(w2.score) + 1 rank
from winners w left outer join winners w2
on w2.category_id = w.category_id and w2.score > w.score
group by w.id
) r
on r.id = winners.id
set winners.rank = r.rank
来源:https://stackoverflow.com/questions/34667070/update-a-mysql-table-with-record-rankings-within-groups