找到 MySQL 的连续范围

find contiguous ranges with MySQL

我有 table 个不同客户 (customer_id) 的值(数字):

CREATE TABLE dev.customer_resources (
  id int(11) NOT NULL AUTO_INCREMENT,
  customer_id int(11) DEFAULT NULL,
  value int(11) DEFAULT NULL,
  PRIMARY KEY (id)
)

我想获取特定客户的号码范围。 所以,如果 table 数据是:

======================
|id|customer_id|value|
======================
| 1|         21|    1|
----------------------
| 2|         21|    2|
----------------------
| 3|         21|    3|
----------------------
| 4|         21|   20|
----------------------
| 5|         21|   21|
----------------------
| 6|         21|   22|
----------------------
| 7|         22|    5|
----------------------

我会得到这样的结果,因为 customer_id=21:

==========
start |end
==========
|    1| 3|
----------
|   20|22|
----------

以及 customer_id=22 的类似内容:

==========
start |end
==========
|    5| 5|
----------

经过搜索解决方案,我找到了以下代码:

select l.value as start,
    (
        select min(a.value) as value
        from customer_resources as a
            left outer join customer_resources as b on a.value = b.value - 1
        where b.value is null
            and a.value >= l.value
    ) as end
from customer_resources as l
    left outer join customer_resources as r on r.value = l.value - 1
where r.value is NULL;

这几乎可以满足我的需要,但不包括将结果限制为特定 customer_id。 我尝试将 customer_id 添加到查询中,但我不太确定在哪里添加它,因为它所做的只是破坏功能。

这似乎是一个 Gaps And Islands 问题。

这里有一些使用变量的老式 MySql 特定解决方案。

通过比较前一个值和 customer_id 找到岛的起点。

SELECT 
 customer_id, 
 value_start as `start`, 
 max(val) as `end`
FROM 
(
    SELECT 
      CASE 
      WHEN `value` = @prev_val + 1 AND customer_id = @prev_grp
      THEN @start_val:= @start_val 
      ELSE @start_val:=`value`
      END AS value_start, 
      @prev_grp:=customer_id as customer_id,
      @prev_val:=`value` as val
    FROM customer_resources
    CROSS JOIN (SELECT @prev_val:=NULL, @start_val:=NULL, @prev_grp:=NULL) AS v
    ORDER BY customer_id, `value`
) AS q 
GROUP BY customer_id, value_start;

LukStorms 解决方案似乎是正确的选择 - 它似乎比绝对必要的要复杂一些...

这是主题的变体...

    DROP TABLE IF EXISTS my_table;

    CREATE TABLE my_table
    (id SERIAL PRIMARY KEY
    ,customer_id int NOT NULL
    ,value int NOT NULL
    );

    INSERT INTO my_table VALUES
    (1,         21,    1),
    (2,         21,    2),
    (3,         21,    3),
    (4,         21,   20),
    (5,         21,   21),
    (6,         21,   22),
    (7,         22,    5);

    SELECT customer_id
         , MIN(value) start
         , MAX(value) end 
      FROM 
         ( SELECT *
                , CASE WHEN value = @prev+1 THEN @i:=@i ELSE @i:=@i+1 END grp
                , @prev:=value prev
             FROM my_table
                , (SELECT @prev:= null,@i:=0) vars 
            ORDER 
               BY customer_id
                , id
         ) x 
     GROUP 
        BY customer_id,grp;
    +-------------+-------+------+
    | customer_id | start | end  |
    +-------------+-------+------+
    |          21 |     1 |    3 |
    |          21 |    20 |   22 |
    |          22 |     5 |    5 |
    +-------------+-------+------+