MySQL:选择连续ID作为范围

时间:2017-09-11 10:49:42

标签: mysql sql

有这个表

+-----+------+
|   ID|Status|
+-----+------+
|    1|     0|
|    2|     1|
|    3|     1|
|    4|     0|
|    5|     1|
|    6|     1|
|    7|     1|
|    8|     1|
|    9|     0|
|   10|     1|
|   12|     1|
+-----+------+

我想选择Status = 1的ID作为连续值的范围:

+-----+-----+
|  Low| High|
+-----+-----+
|    2|    3|
|    5|    8|
|   10|   10|
|   12|   12|
+-----+-----+

我不确定如何处理这个问题。

3 个答案:

答案 0 :(得分:2)

您可以通过为每一行分配一个组来完成此操作。一种方法使用相关子查询来计算任何给定的不同状态的数量:

select status, min(id) as low, max(id) as high
from (select t.*,
             (select count(*)
              from t t2
              where t2.id < t.id and t2.status <> t.status
             ) as grp
      from t
     ) t
group by status, grp;

您可以将where添加到内部或外部查询,以限制为status = 1

编辑:

如果性能是个问题,那么变量会更快。我认为最简单的方法是这里建议的版本 - 计算非1值的数量:

select status, min(id) as low, max(id) as high
from (select t.*,
             (@grp := (status <> 1)) as grp
      from t cross join 
           (select @grp := 0) params
      order by id
     ) t
where status = 1
group by status, grp;

这也可以使用t(id, status)上的索引。

答案 1 :(得分:0)

尝试使用变量和GROUP BY

SELECT MIN(id) AS low ,MAX(id) AS high
FROM
(
    SELECT 
        @row_number:=CASE
            WHEN @id = status THEN @row_number
            ELSE @row_number + 1
        END AS num,
        @id:=status as status,
        id
    FROM tab, (SELECT @row_number:=0, @id := 1) t
    ORDER BY id
) t
WHERE t.status = 1
GROUP BY num

demo

答案 2 :(得分:0)

抱歉,误读MySQL for MSSQL。似乎MySQL不支持递归CTE。

使用递归CTE:

declare @data table(
    ID int not null primary key,
    Status bit not null)

insert into @data
values
(1, 0),
(2, 1),
(3, 1),
(4, 0),
(5, 1),
(6, 1),
(7, 1),
(8, 1),
(9, 0),
(10, 1),
(12, 1)

;
with dataRecursive as(
    -- anchor
    select      data.ID low,
                data.ID
    from        @data data
    where       data.Status = 1
                and
                not exists(
                    select      1
                    from        @data data_previous
                    where       data_previous.Status = 1
                                and
                                data_previous.ID = data.ID -1
                )
    union all
    -- recursion
    select      dataRecursive.low,
                data_next.ID
    from        @data data_next
                inner join
                dataRecursive
                on  dataRecursive.ID = data_next.ID - 1
    where       data_next.Status = 1
)

select      low,
            MAX(ID) as high
from        dataRecursive
group by    low
order by    low