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

时间:2016-08-21 16:56:09

标签: mysql mysql-variables

我需要根据该part_id的build_steps的MAX值创建一个排名列,其中每个不同的part_id的排名与order_id相关联。每当迭代一个新的order_id时,都应重新开始排名。

我有以下小提琴,但它没有正确创造排名。

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;

表:

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

1 个答案:

答案 0 :(得分:1)

您的问题很好地证明了依赖于引擎执行顺序的会话变量用于此类任务是不稳定的。在您的情况下,似乎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

至少在使用MySQL 5.6的sqlfiddle上有效。然而,人们已经报告了问题,较新的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