每个组/分区的MySQL密集等级

时间:2019-03-10 11:47:40

标签: mysql sql group-by dense-rank

我正在尝试构建一种密集等级的功能,但不完全是密集等级。我想对每个组进行排名,但是在每个组中,我想对相同的值保持相同的排名。我编写了以下查询,它为我提供了一种密集等级的功能

SELECT  id, item_code
    @curRank := @curRank + 1 AS rank
FROM table t, (SELECT @curRank := 0) r
WHERE <several filters>

enter image description here

我想要这个

enter image description here

我找到的最接近的问题是这个MySQL give a rank to each group

请帮助。

3 个答案:

答案 0 :(得分:2)

使用变量的正确方法是:

SELECT id,item_code,
       (@rn := if(@id = id,
                  if(@ic = item_code, @rn,  -- do not increment
                     if(@ic := item_code, @rn + 1, @rn + 1)
                    ),
                  if(@id := id,
                     if(@ic := @item_code, 1, 1                        
                       ), 1
                     if(@ic := @item_code, 1, 1                        
                       )
                    )
                 )                         
       ) as dense_rank
FROM table t CROSS JOIN
     (SELECT @rn := 0, @id := '', @ic := '') params
WHERE <several filters>
ORDER BY item_code;

两件事:

  • 在一个表达式中分配变量并在另一个表达式中使用它可能会导致问题。您需要在单个表达式中分配和使用变量。 MySQL不保证表达式的求值顺序。
  • ORDER BY非常重要。

您还可以使用相关子查询来表达这一点:

SELECT id, item_code
        (SELECT COUNT(DISTINCT item_code)
         FROM table t2
         WHERE . . . AND
               t2.item_code <= t.item_code
        ) as rank
FROM table t
WHERE <several filters>

答案 1 :(得分:2)

您可以在复合PRIMARY KEY中使用AUTO_INCREMENT rank列创建(临时) MyISAM 表。这样,当您使用不同的rank组合填充表格时,ID(id, item_code)将有一个序列。然后,您可以将该表与原始数据连接起来。

CREATE TEMPORARY TABLE tmp_rank(
  id INT,
  item_code varchar(50),
  rank INT AUTO_INCREMENT,
  PRIMARY KEY (id, rank)
) engine=MyISAM
  SELECT DISTINCT NULL as rank, id, item_code
  FROM test t
  -- WHERE <several filters>
  ORDER BY id, item_code
;

SELECT t.*, x.rank
FROM tmp_rank x
JOIN test t USING(id, item_code)
-- WHERE <several filters>

Demo

如果要在单个查询中进行查询,可以尝试以下操作:

SELECT id, item_code,
    CASE 
      WHEN id = @curId AND item_code = @curCode
        THEN @curRank
      WHEN id <> @curId THEN @curRank := 1
      ELSE @curRank := @curRank + 1
    END  AS rank,
    @curId := id,
    @curCode := item_code
FROM test t
CROSS JOIN (SELECT
    @curRank := 0,
    cast(@curCode := null as signed),
    cast(@curId   := NULL as char)
) r
#WHERE <several filters>
ORDER BY id, item_code

Demo

请注意,未定义SQL语句中操作的评估顺序。因此,当您在同一条语句中读取和写入用户变量时,您将继续介绍实现细节。

MySQL 8 MariaDB 10.2 中,它将是:

SELECT  id, item_code,
    DENSE_RANK() OVER (PARTITION BY id ORDER BY item_code) as `rank`
FROM test t
-- WHERE <several filters>

Demo

答案 2 :(得分:1)

仅当@cur_rank更改时,才需要递增item_code,而当1更改时,则将其重新设置为id

SELECT  id, item_code,
    @curRank := IF(@curId = id, 
                    IF(@curItem = item_code, @curRank, @curRank + 1),
                    1) AS rank,
    @curId := id, @curItem := item_code
FROM table t, (SELECT @curRank := 0, @curId := 0, @curItem := null) r
WHERE <several filters>
ORDER BY id, item_code