MySQL 设置第一列和第二列的排名

MySQL SET rank over First Column and Second Column

我正在尝试按第一列和第二列创建查询设置排名列。就像 Rank over Partition 不存在 MySQL

例如,

来自

+----+-------+--------+------+
| id | First | Second | Rank |
+----+-------+--------+------+
|  1 | a     |     10 |      |
|  2 | a     |      9 |      |
|  3 | b     |     10 |      |
|  4 | b     |      7 |      |
|  5 | a     |      1 |      |
|  6 | b     |      1 |      |
+----+-------+--------+------+

+----+-------+--------+------+
| id | First | Second | Rank |
+----+-------+--------+------+
|  1 | a     |     10 |    3 |
|  2 | a     |      9 |    2 |
|  3 | b     |     10 |    3 |
|  4 | b     |      7 |    2 |
|  5 | a     |      1 |    1 |
|  6 | b     |      1 |    1 |
+----+-------+--------+------+

排名没有继续。当到达'First'列的'a'的最后一个值时,它又从1开始。

而且必须是 SET 而不是 SELECT。 我不介意使用 SELECT,但我的意思是我不是要从数据库中检索数据,而是要设置值。

提前为队友干杯。

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,first CHAR(1) NOT NULL
,second INT NOT NULL
);

INSERT INTO my_table VALUES
(1,'a',10),
(2,'a',9),
(3,'b',10),
(4,'b',7),
(5,'a',1),
(6,'b',1);

SELECT id
     , first
     , second
     , rank 
  FROM 
     ( SELECT x.* 
            , CASE WHEN @prev = first THEN @i:=@i+1 ELSE @i:=1 END rank
            , @prev:=first 
         FROM my_table x
            , (SELECT @prev:=null,@i:=0) vars 
        ORDER 
           BY first
            , second
            , id
     ) a
 ORDER 
    BY id;
+----+-------+--------+------+
| id | first | second | rank |
+----+-------+--------+------+
|  1 | a     |     10 |    3 |
|  2 | a     |      9 |    2 |
|  3 | b     |     10 |    3 |
|  4 | b     |      7 |    2 |
|  5 | a     |      1 |    1 |
|  6 | b     |      1 |    1 |
+----+-------+--------+------+
6 rows in set (0.00 sec)

一种方法是相关子查询。对于 rank() 你可以这样做:

select t.*,
       (select count(*) + 1
        from t t2
        where t2.first = t.first and t2.second < t.second
       ) as rank
from t;

排名很难用变量处理(dense_rank()row_number() 更简单)。

编辑:

这很容易变成 update:

update t join
       (select t.*,
               (select count(*) + 1
                from t t2
                where t2.first = t.first and t2.second < t.second
               ) as new_rank
        from t
       ) tt
       on t.id = tt.id
    set t.rank = tt.new_rank;

提出了我正在寻找的解决方案。 我不确定这些查询是否完全安全,但到目前为止没有危害。

SET @rank = 0, @First = ''
UPDATE 'Table' SET 
rank = IF(@First = First, @rank:= @rank +1, @rank := 1 AND @First := First)
ORDER BY First ASC, Second;