更新 MySQL table 记录组内排名

Update a MySQL table with record rankings within groups

我有一个名为 'winners' 的 table,其中包含列 'id'、'category'、'score'、'rank'.

我需要更新我的 table 并在子类别 (category_id) 中分配一个排名,根据示例是 2,但将来可能会更多。

我发现的大多数答案都是基于 select 语句,这些语句往往只是输出 table 视图,但我确实找到了一个非常好的 'Update' 答案() 特别是答案更新,其中需要领带才能共享相同的排名。

样本

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 |      |
+----------+-------------+-------+------+

当只有一个类别需要担心但有多个类别或子组需要排名时,上面的链接答案非常适合数据。

我试图在代码中添加 where 子句(第 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;

试图对每个 运行 代码两次 category_id 但脚本失败。

任何关于为多个类别修改上述答案的选项都很棒。

需要的结果只是为了澄清(在类别中排名)。

+----------+-------------+-------+------+
|       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 |
+----------+-------------+-------+------+

谢谢大家!

更新

设法找到另一段代码 ,我最初以某种方式错过了它,并且能够使用每个 category_id 的 where 子句成功修改它。这不是一种理想的方式 - 运行多次针对多个类别进行操作,但此时它很好。

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

更多 'automatic' 在 1 个脚本中处理排名中的多个类别的更多答案 运行 仍然非常欢迎和赞赏。

谢谢

更新

刚注意到我找到的答案并没有很好地处理零分 (0.00),而是将它们排在其他分数的中间。

shawnt00 下面的答案有效并正确评估了零分。

update winners
set rank = (
    select count(score) + 1
    from winners w2
    where w2.category_id = winners.category_id and w2.score > winners.score
)

count(*) 即使没有行符合条件,即最高排名,也会评估为零。如果你想要一个密集的排名,你可以这样做:

update winners
set rank = (
    select count(distinct score) + 1
    from winners w2
    where w2.category_id = winners.category_id and w2.score > winners.score
)

编辑:根据您的评论,我发现了一个有效的查询。 (它适用于 SQL 服务器,我不熟悉 MySQL 的怪癖。)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