创建没有光标的ID范围

时间:2018-12-10 04:48:29

标签: sql sql-server tsql gaps-and-islands sql-server-2017

不确定是否已经问过这个问题或类似问题,但是我找不到一个问题。

在不更改值的情况下创建ID范围的要求。可以使用此架构:

declare @mytable as table(ID int, Val int)

insert into @mytable values
(1,     1),
(2,     1),
(3,     1),
(4,     2),
(5,     2),
(6,     2),
(7,     2),
(8,     1),
(9,     1),
(10,    1),
(11,    4),
(12,    4),
(13,    4),
(14,    4),
(15,    4),
(16,    5);

预期结果将是

StartID     EndID   Val
1           3       1
4           7       2
8           10      1
11          15      4
16          16      5

现在,我可以通过运行游标来实现此目的,如果n情况下记录数为数百万,我认为游标会变慢。我希望可以使用一些复合查询来编写它,但无法弄清楚该怎么做。

因此,我在编写此类查询时需要帮助,不用说,这不是不是学校/拼贴项目/作业。

2 个答案:

答案 0 :(得分:2)

这是一个场景,您尝试根据Val中的更改将记录分组在一起。

这是使用window functions确定Val何时更改的,并分配island_nbr

答案:

select min(b.ID) as StartID
, max(b.ID) as EndID
, max(b.Val) as Val
from (
    select a.ID
    , a.Val
    , sum(a.is_chng_flg) over (order by a.ID asc) as island_nbr
    from (
        select m.ID
        , m.Val
        , case lag(m.Val, 1, m.Val) over (order by m.ID asc) when m.Val then 0 else 1 end is_chng_flg
        from @mytable as m
        ) as a
    ) as b
group by b.island_nbr --forces the right records to show up
order by 1

答案 1 :(得分:1)

这是一个孤岛问题。但是最简单的方法是行号的区别:

select min(id) as startId, max(id) as endId, val
from (select t.*,
             row_number() over (order by id) as seqnum,
             row_number() over (partition by val order by id) as seqnum_v
      from @mytable t
     ) t
group by (seqnum - seqnum_v), val
order by startId;