SQL 按原始顺序划分

SQL partition by with original order

原文如下MySQLtable:

+----+-----+
| Id | Num |
+----+-----+
| 1  |  1  |
| 2  |  1  |
| 3  |  1  |
| 4  |  2  |
| 5  |  1  |
| 6  |  2  |
| 7  |  2  |
+----+-----+

当我使用 select Id, Num, row_number() over(partition by Num) from t 时,MySQL 会自动打乱 Num 列的顺序。但是,我想保持 Num 列顺序不变。 具体来说,理想的输出应该是这样的:

+----+-----+-----+
| Id | Num | row |
+----+-----+-----+
| 1  |  1  |  1  |
| 2  |  1  |  2  |
| 3  |  1  |  3  |
| 4  |  2  |  1  |
| 5  |  1  |  1  |
| 6  |  2  |  1  |
| 7  |  2  |  2  |
+----+-----+-----+

如何编写此 MySQL 查询?

这是一个 gaps-and-islands 问题。我建议使用行号之间的差异来识别组。

如果 id 总是递增而没有间隙:

select id, num, 
    row_number() over(partition by num, id - rn order by id) rn
from (
    select t.*, row_number() over(partition by num order by id) rn
    from mytable t
) t
order by id

否则,我们可以用另一个 row_number():

生成我们自己的增量 id
select id, num, 
    row_number() over(partition by num, rn1 - rn2 order by id) rn
from (
    select t.*, 
        row_number() over(order by id) rn1,
        row_number() over(partition by num order by id) rn2
    from mytable t
) t
order by id

Demo on DB Fiddle - 对于您的示例数据,两个查询都会产生:

id | num | rn
-: | --: | -:
 1 |   1 |  1
 2 |   1 |  2
 3 |   1 |  3
 4 |   2 |  1
 5 |   1 |  1
 6 |   2 |  1
 7 |   2 |  2

您可以通过 writing your own row_number 执行此操作以更好地控制其分区。

set @prev_num = null;
set @row_number = 0;

select
  id,
  -- Reset row_number to 1 whenever num changes, else increment it.
  @row_number := case
    when @prev_num = num then 
      @row_number + 1
    else
      1
    end as `row_number`,
  -- Emulate lag(). This must come after the row_number.
  @prev_num := num as num
from foo
order by id;

与 Schwern 提出的解决方案的想法相同。 MySQL 中的另一种语法风格,我发现它非常简单易用。

Select
    id
  , num
  , value
from
(select
      T.id,
      T.num,
      if( @lastnum = T.num, @Value := @Value + 1,@Value := 1) as Value,
      @lastnum := T.num as num2
   from
      mytable T,
      ( select @lastnum := 0,
               @Value := 1 ) SQLVars
   order by
      T.id) T;

DB fiddle link - https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=e04692841d091ccd54ee3435a409c67a