Compute the region of specific value inside a dataset

-- take the data below for example, we can see that records from 2 to 3 is 0, and records from 8 to 10 is also 0,
-- and we want to answer this question using SQL
declare @t table(seqno int identity, val int)
insert @t(val) select 5
insert @t(val) select 0
insert @t(val) select 0
insert @t(val) select 1
insert @t(val) select 1
insert @t(val) select 2
insert @t(val) select 2
insert @t(val) select 0
insert @t(val) select 0
insert @t(val) select 0

/* pass 1: analysis the raw data

select 'a.seqno'=a.seqno, 'a.val'=a.val
    ,'b.seqno'=b.seqno, 'b.val'=b.val
from @t a cross join @t b
where a.val = 0

-- pass 1 output
a.seqno     a.val       b.seqno     b.val       
----------- ----------- ----------- ----------- 
2           0           1           5
2           0           2           0
2           0           3           0
2           0           4           1
2           0           5           1
2           0           6           2
2           0           7           2
2           0           8           0
2           0           9           0
2           0           10          0
3           0           1           5
3           0           2           0
3           0           3           0
3           0           4           1
3           0           5           1
3           0           6           2
3           0           7           2
3           0           8           0
3           0           9           0
3           0           10          0
8           0           1           5
8           0           2           0
8           0           3           0
8           0           4           1
8           0           5           1
8           0           6           2
8           0           7           2
8           0           8           0
8           0           9           0
8           0           10          0
9           0           1           5
9           0           2           0
9           0           3           0
9           0           4           1
9           0           5           1
9           0           6           2
9           0           7           2
9           0           8           0
9           0           9           0
9           0           10          0
10          0           1           5
10          0           2           0
10          0           3           0
10          0           4           1
10          0           5           1
10          0           6           2
10          0           7           2
10          0           8           0
10          0           9           0
10          0           10          0
*/

/* pass 2: calc start and end of a range, base on data retrieve on pass 1, let's find out how the compute the start of the range

CASE 1:
a.seqno     a.val       b.seqno     b.val       
----------- ----------- ----------- ----------- 
2           0           1           5
2           0           2           0 -- target
2           0           3           0
2           0           4           1
2           0           5           1
2           0           6           2
2           0           7           2
2           0           8           0
2           0           9           0
2           0           10          0

for a.seqno=2, calc range_start:
    for all b.seqno < a.seqno and b.val <> 0
        (b.seqno=1,b.val=5),
    select the max(b.seqno) (in this case, value = 1) + 1, so range_start = 2

CASE 2:
a.seqno     a.val       b.seqno     b.val       
----------- ----------- ----------- ----------- 
9           0           1           5
9           0           2           0
9           0           3           0
9           0           4           1
9           0           5           1
9           0           6           2
9           0           7           2
9           0           8           0 -- target
9           0           9           0
9           0           10          0

for a.seqno=9, calc range_start:
    for all b.seqno < a.seqno and b.val <> 0
        (b.seqno=1,b.val=5),
        (b.seqno=4,b.val=1),
        (b.seqno=5,b.val=1),
        (b.seqno=6,b.val=2),
        (b.seqno=7,b.val=2),
    select the max(b.seqno) (in this case, value = 7) + 1, so range_start = 8
*/
select a.seqno, a.val
    ,range_start=max(case when b.seqno < a.seqno and b.val <> 0 then b.seqno else null end)+1
    ,range_end=min(case when b.seqno > a.seqno and b.val <> 0 then b.seqno else null end)-1
from @t a cross join @t b
where a.val = 0
group by a.seqno, a.val
/* pass 2 output
seqno       val         range_start range_end   
----------- ----------- ----------- ----------- 
2           0           2           3
3           0           2           3
8           0           8           NULL
9           0           8           NULL
10          0           8           NULL
*/

/* pass 3:
    in pass 2, if first/last record is 0, then the range_start/range_end will be examined to the value NULL,
    so let's fix this bug.
*/
select a.seqno, a.val
    ,range_start=isnull(max(case when b.seqno < a.seqno and b.val <> 0 then b.seqno else null end)+1, min(b.seqno))
    ,range_end=isnull(min(case when b.seqno > a.seqno and b.val <> 0 then b.seqno else null end)-1, max(b.seqno))
from @t a cross join @t b
where a.val = 0
group by a.seqno, a.val
/* pass 3 output
seqno       val         range_start range_end   
----------- ----------- ----------- ----------- 
2           0           2           3
3           0           2           3
8           0           8           10
9           0           8           10
10          0           8           10
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值