更新具有组内记录排名的MySQL表

时间:2016-01-07 23:30:21

标签: mysql sql-update ranking ranking-functions

我有一个名为'获奖者'的表格,其中列'id','category','score','rank'。

我需要更新我的表并在子类别(category_id)中设置一个等级,根据样本为2但可能比将来更多。

我发现的大多数anwers都是基于select语句,它只是简单地输出表格视图,但我确实找到了一个非常好的'更新'答案(https://stackoverflow.com/a/2727239/4560380),特别是需要联系的答案更新分享相同的排名。

样品

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

谢谢大家!

更新

管理找到我原先错过的另一段代码https://stackoverflow.com/a/13270603/4560380,并且能够成功修改每个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

在1个脚本运行中对排名中的多个类别进行更“自动”处理的进一步答案仍然非常受欢迎和赞赏。

由于

更新

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

下面的shawnt00答案正在运行并正确评估零分数。 https://stackoverflow.com/a/34667112/4560380

1 个答案:

答案 0 :(得分:0)

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 Server,我不熟悉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