SQL Server - 按标志汇总日期

时间:2018-06-05 21:04:46

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

我正在使用SQL Server 2008(如果需要,可以访问SQL 2017)并且有一个这样的表:

DECLARE @tbl TABLE (recdate DATE, myflag BIT)

该表包含范围内所有日期的行,myflag位将关闭和打开,如下所示:

recdate    | myflag
2017-01-01 | 1
2017-01-02 | 1
2017-01-03 | 1
...
2017-04-03 | 1
2017-04-04 | 0
2017-04-05 | 0
..
2017-05-15 | 0
2017-05-16 | 1
etc.

但我真正需要的是

period_from | period_to  | myflag
2017-01-01  | 2017-04-03 | 1
2017-04-04  | 2017-05-15 | 0
2017-05-16  | 2017-05-21 | 1

所以每当myflag改变时,它会创建一个新行,前一行设置结束日期(如果有意义的话)

我确信有一种非常明显的方式可以做到这一点,但是我已经准备好把头撞到墙上了......我已经选择和子选择来回走动了插入和更新到临时表,甚至尝试光标(我知道!但它是一次性查询)

1 个答案:

答案 0 :(得分:5)

这是一个缺口和岛屿问题。为此,您可以使用行号的差异:

select min(recdate) as period_from, max(recdate) as period_to, flag
from (select t.*,
             row_number() over (order by recdate) as seqnum,
             row_number() over (partition by flag order by recdate) as seqnum_f
      from @tbl t
     ) t
group by (seqnum - seqnum_f), flag;

为什么这样做有点难以用语言解释。我发现如果你运行子查询,你就会明白为什么你所寻找的组的差异是不变的。

如果您的日期是连续的,没有间隙或重复或时间组件,您可以稍微简化:

select min(recdate) as period_from, max(recdate) as period_to, flag
from (select t.*,
             dateadd(day, 
                     - row_number() over (partition by flag order by recdate
                     recdate
                    ) as grp
      from @tbl t
     ) t
group by grp, flag;

这与第一个版本的逻辑基本相同。