用MySQL查找连续范围

时间:2018-07-10 17:49:04

标签: mysql sql gaps-and-islands

我有一个不同客户(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)
)

我想获取特定客户的数字范围。 因此,如果表数据为:

======================
|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添加到查询中,但是我不确定在哪里添加它,因为它所做的只是中断功能。

2 个答案:

答案 0 :(得分:2)

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 |
    +-------------+-------+------+

答案 1 :(得分:1)

这似乎是一个缺口和孤岛的问题。

这里有一些使用变量的老式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;