定义范围以覆盖数字序列中的间隙(T-SQL)

时间:2012-11-30 09:51:55

标签: sql sql-server sql-server-2008 tsql

简化我的问题 - 我有6位数的字段,从1开始到999999为客户分配号码。大多数号码是按顺序分配的,但是号码可以由用户手动分配,并且此功能已用于整个范围内的不可预测的模式。

我们现在需要识别尚未分配的数字(简单) - 然后将其转换为多个范围(看起来很复杂)。

例如,鉴于已指定以下数字

1,2,3,4,5,
1001,1002,1003,1004,1005,
999101,999102,999103,999104,999105

我需要一组结果范围,例如

Start  End
6      1000
1006   999100
999106 999999

到目前为止,我的想法是,在查询中写入可能过于复杂 - 最好通过循环从1到999999,并将范围添加到临时表。

有兴趣听到我能想象的想法有一些方法。我正在使用SQL Server 2008 R2。这是一次性练习,因此即使是非SQL解决方案也可能是合适的,例如,如果这可以在Excel中轻松完成。

6 个答案:

答案 0 :(得分:5)

试试这个

declare @t table (num int)

insert @t values (2),(3),(6),(7),(9),(10),(11)

select 
    MIN(number) as rangestart,
    MAX(number) as rangeend
from
(
    select *,
        ROW_NUMBER() over (order by number) -
        ROW_NUMBER() over (order by num,number) grp
    from 
    (   
        select number from master..spt_values where type='p' and number between 1 and 15 
    ) numbers 
    left join @t t
        on numbers.number = t.num       
) v
where num is null
group by grp

参考:itzik ben-gan的空白和岛屿

创建高达999999的数字查询

select p1.number +  p2.number * 2048 as number
from 
(select * from master..spt_values where type='p' ) p1,
(select * from master..spt_values where type='p' and number<489) p2    
where p1.number +  p2.number * 2048 <=999999

答案 1 :(得分:2)

declare @t table (num int)

insert @t values 
(2),(3),(4),(5),
(1001),(1002),(1003),(1004),(1005),
(999101),(999102),(999103),(999104),(999105)

;with cte as
(
    select num,(ROW_NUMBER() OVER(ORDER BY num)) + 1 as idx from @t
    union
    select 0 [num],1 [idx] --start boundary
    union
    select 1000000 [num],COUNT(num) + 2 [idx] from @t --end boundary
)
select c1.num + 1 [Start], c2.num - 1 [End] 
from cte c1
inner join cte c2 on c2.idx = c1.idx + 1
where c2.num != c1.num + 1

答案 2 :(得分:1)

create table #temp (id int)

insert into #temp (id)
values (1),(2),(3),(1000),(1001),(1002),(2000)

--drop table #temp
with cte as
(
select *, ROW_NUMBER() over(order by id) as rn
from #temp a 
)
select a.id + 1, b.id - 1
from cte a join cte b on a.rn = b.rn - 1 and a.id <> b.id -1

它不包括尾部范围,如2001-9999

答案 3 :(得分:1)

这是SQLFiddle demo

select 

case when max(n1)=0 then 1 else max(n1)end,
case when max(n2)=0 then 999999 else max(n2)end

from
(
select t.n+1 as n1,0 n2,
   row_number() over(order by t.n)
      +isnull((select 0 from t where n=1),1)  
      rn  
from t
left join t t2 on t.n+1=t2.n
where t2.n is null

union all 

select 0 n1, t.n-1 as n2 ,
row_number() over(order by t.n) rn 
from t
left join t t2 on t.n-1=t2.n
where t2.n is null
  and t.n>1
) t3
group by rn

答案 4 :(得分:1)

declare @t table(id int)
insert @t values
(1),(2),(3),(4),(5),(1001),(1002),(1003),(1004),(1005),
(999101),(999102),(999103),(999104),(999105)

select t1.id+1 [start], coalesce(t3.[end], 999999) [end] 
from @t t1
left join @t t2 on t1.id +1 = t2.id
cross apply
(select min(id)-1 [end] from @t where t1.id < id
) t3
where t2.id is null

答案 5 :(得分:-1)

如果您有一个名为“ kh ”的表格,例如列“ myval ”,这是您的整数列表,您可以尝试此SELECT。

SELECT MAX(t1.myval+1) AS 'StartRange',t3.myval-1 AS 'EndRange' 
FROM kh t1, kh t3
WHERE t1.myval+1 NOT IN (SELECT myval FROM kh t2 ORDER BY myval)
AND t3.myval-1 NOT IN (SELECT myval FROM kh t4 ORDER BY myval)
AND t1.myval < t3.myval
GROUP BY  t3.myval
ORDER BY StartRange