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
原文如下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