SQL检测"序列中断"

时间:2015-11-15 10:16:25

标签: mysql sql

我有数据,其中增量序列在某处被破坏,可能是多次。 例如。 (2,3,4,5,6,8,10)。

我想得到:

  1. 第一个"破坏"地方(6是示例中的最后一个好处)
  2. "破坏"地方(2次,7次和9次)
  3. 使用SQL(最好是通用的,适用于oracle和mysql以及其他sql平台)。

    使用序列或auto_increment是特定于平台的。

    我尝试过像

    这样的自连接构造
    select curr.id+1 as first_fail from junk as prev
    join junk as curr 
    on (prev.id+1 = curr.id) 
    order by curr.id desc limit 1;
    

    http://sqlfiddle.com/#!9/bae781/4/0) 但它看起来很丑陋,并且无法获得"破坏"这样放置。

4 个答案:

答案 0 :(得分:3)

检查一下:

select prev_id+1 as first_fail,count(*)-1 as total_broken from
(
   select curr.id as curr_id,prev.id as prev_id
   from junk as prev
   left join junk as curr  on prev.id+1 = curr.id
) sub where sub.curr_id is null ;

SQL小提琴示例:http://sqlfiddle.com/#!9/bae781/62

查询Gap是否大于1

select p2_id-1 as first_fail,sum(broken) as total_broken from
(select p1.row_num as p1_row,p2.row_num as p2_row,p1.id as p1_id,p2.id as p2_id,(p2.id-p1.id-1) as broken from
( 
  select @row_num:=@row_num+1 as row_num,junk.id
  from junk,(select @row_num:=0) s1
) p1
left join
( 
  select @row_num2:=@row_num2+1 as row_num,junk.id
  from junk,(select @row_num2:=0) s2
) p2 on p1.row_num+1=p2.row_num ) as sub
where broken is not null and broken > 0;

SQL小提琴演示:http://sqlfiddle.com/#!9/974ec/1

答案 1 :(得分:2)

您可以生成计数表并使用LEFT JOIN

SELECT  MIN(SeqValue)-1 AS `first`, COUNT(*) AS `total`
FROM (
  SELECT
    (TWO_1.SeqValue + TWO_2.SeqValue + TWO_4.SeqValue + TWO_8.SeqValue + TWO_16.SeqValue) SeqValue
   FROM
    (SELECT 0 SeqValue UNION ALL SELECT 1 SeqValue) TWO_1
    CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 2 SeqValue) TWO_2
    CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 4 SeqValue) TWO_4
    CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 8 SeqValue) TWO_8
    CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 16 SeqValue) TWO_16
)  AS tally
LEFT JOIN  junk
ON junk.id = tally.SeqValue
WHERE tally.SeqValue >= (SELECT MIN(id) FROM junk)
 AND tally.SeqValue <= (SELECT MAX(id) FROM junk)
 AND junk.id IS NULL;

SqlFiddleDemo

有很多方法可以生成计数表(CTE/recursive CTE/subquery/table function/variables/windowed function)。

您可以轻松地交换计数子查询(我的示例仅提供有限的范围)。

修改

MySQL快速扩展它的一种方法是use multiple CROSS JOIN

SELECT  MIN(SeqValue)-1 AS `first`, COUNT(*) AS `total`
FROM (select (@rn := @rn + 1) - 1 as SeqValue
      from (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d1 cross join 
         (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d2 cross join 
         (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d3 cross join 
         (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d4 cross join 
         (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d5 cross join 
         (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d6 cross join
         (select @rn := 0) params
)  AS tally
LEFT JOIN  junk
ON junk.id = tally.SeqValue
WHERE tally.SeqValue >= (SELECT MIN(id) FROM junk)
 AND tally.SeqValue <= (SELECT MAX(id) FROM junk)
 AND junk.id IS NULL;

SqlFiddleDemo2

答案 2 :(得分:1)

这是在寻找之前跳过了多少个id,以及最后一次是什么:

SET @i:=0, @last:=0;
SELECT id, skipped_before, last_correct FROM (
    SELECT id, 
           @i:=@i+1 row_num, 
           id-@i-1 skipped_before, 
           if(id-@i=1,@last:=id,@i:=@i+id-@last-1), 
           @last last_correct
    FROM junk
) a
WHERE skipped_before;

SQLFiddle

断链计数是skipped_berore>0计数的行 First fail显然是最小的last_correct+1 错过的ID计数为sum(skipped_before),   但是这可以通过

更容易找到
SELECT MAX(id)-MIN(id)-COUNT(id)+1 FROM junk;

答案 3 :(得分:1)

按ids排序的所有开头“中断”:

select j1.id + 1 as id
from junk j1
left join junk j2 on j2.id = j1.id + 1
where j2.id is null
    and j1.id <> (select max(id) from junk)
order by j1.id;

选择第一行以获得第一个“休息”。计算行数以获得“休息”的数量。

如果您需要所有缺失ID的数量:

-- get number of missing ids
select 
    -- num rows you should have
    (select max(id) from junk) - (select min(id) from junk)     + 1
    -- num rows you really have
    - count(*) as num_missings
from junk;

或更短:

select max(id) - min(id) + 1 - count(*) as num_missings from junk;