根据属于组集的不同列的最大值创建排名列

Create a Rank Column based on the max values of a distinct column that belongs to a group set

我需要创建一个排名列,其中每个不同的 part_id 与 order_id 相关联,基于 build_steps 的最大值 [=29] =].每当迭代新的 order_id 时,排名应该重新开始。

我有以下 fiddle 但它没有正确创建排名。

http://sqlfiddle.com/#!9/63d47/29

下面是我的查询

SET @current_rank = 0;
SET @prevOrder = null;
SELECT part_id, MAX(build_steps) AS max_build_steps, 
   @current_rank:= CASE WHEN @prevOrder = order_id 
             THEN  @current_rank:=@current_rank +1 
             ELSE @current_rank:=1 END  rank,
              @prevOrder:= order_id as 'order_id'
FROM orders,
(SELECT @current_rank:=0) r   
GROUP BY order_id, part_id
ORDER BY order_id desc, max_build_steps desc;

Table:

CREATE TABLE orders
    (`part_id` int, `build_steps` int, `order_id` int)
;

INSERT INTO orders
    (`part_id`, `build_steps`, `order_id`)
VALUES
    (234554, 1, 1234),
    (234554, 2, 1234),
    (234554, 3, 1234),
    (234554, 4, 1234),
    (234554, 5, 1234),
    (234554, 6, 1234),
    (234554, 7, 1234),
    (234554, 8, 1234),
    (234555, 1, 1234),
    (234555, 2, 1234),
    (234556, 1, 1234),
    (234556, 2, 1234),
    (234556, 3, 1234),
    (234557, 1, 1234),
    (234566, 1, 5678),
    (234566, 2, 5678),
    (234566, 3, 5678),
    (234566, 4, 5678),
    (234566, 5, 5678),
    (234567, 1, 5678),
    (234567, 2, 5678),
    (234568, 1, 5678),
    (234569, 1, 5678)
;

预期结果:

part_id, max_build_steps, rank, order_id

234566  5   1   5678

234567  2   2   5678

234568  1   3   5678

234569  1   4   5678

234554  8   1   1234

234556  3   2   1234

234555  2   3   1234

234557  1   4   1234

当前查询结果:

part_id max_build_steps rank    order_id

234566  5   1   5678

234567  2   2   5678

234568  1   3   5678

234569  1   4   5678

234554  8   1   1234

234556  3   3   1234

234555  2   2   1234

234557  1   4   1234

你的问题很好地证明了使用会话变量来完成依赖于引擎执行顺序的任务是可行的。在您的情况下, MySQL 似乎在对结果集进行排序之前在 SELECT 部分中执行了您的操作。解决方法是使用有序子查询:

select part_id,
  max_build_steps,
  case when order_id = @order_id 
    then @rank := @rank + 1
    else @rank := 1
  end as rank,
  @order_id := order_id as order_id
from (
  select o.part_id, o.order_id, max(build_steps) as max_build_steps
  from orders o
  group by o.part_id, o.order_id
  order by o.order_id, max_build_steps desc
  limit 1000000000
) sub 
cross join (select @order_id := null, @rank := 0) init_session_vars

这至少适用于 sqlfiddle 和 MySQL 5.6。然而,人们已经报告了问题,较新的 MySQL 版本不对子查询结果进行排序。这就是为什么我添加了一个巨大的限制,这是 MariaDB 的一个解决方法(不能说它是否适用于 MySQL 5.7)。

但是 - 您最好获取有序结果并使用保证执行顺序的过程语言计算排名。

更新

这是使用临时表和 AUTO_INCREMENT 列的另一种方法:

create temporary table tmp1 (
  ai int auto_increment primary key,
  part_id int, 
  order_id int,
  max_build_steps int
) select o.part_id, o.order_id, max(build_steps) as max_build_steps
  from orders o
  group by o.part_id, o.order_id
  order by o.order_id, max_build_steps desc
;

create temporary table tmp2 (order_id int, min_ai int)
  select order_id, min(ai) as min_ai
  from tmp1
  group by order_id
;

select t1.part_id,
  t1.order_id,
  t1.max_build_steps,
  t1.ai - t2.min_ai + 1 as rank
from tmp1 t1
join tmp2 t2 using(order_id);

sqlfiddle