创建没有光标的 ID 范围

Create range of ID without cursor

不确定是否有人问过这个或类似的问题,但我找不到。

在不更改值的情况下创建 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 情况下记录数将达到数百万,我认为游标会更慢。我希望它可以使用一些复合查询来编写,但不知道如何编写。

所以我需要帮助来编写那种查询,不用说了,它是 not a school/collage project/assignment.

这是一个 场景,您尝试根据 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

这是一个缺口和孤岛问题。但最简单的方法是行号的不同:

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;