【问题标题】:Overlapping between first record enddate and next record start date in SQL ServerSQL Server 中第一条记录结束日期和下一条记录开始日期之间的重叠
【发布时间】:2020-11-03 08:12:06
【问题描述】:

我有以下类型的数据,我需要以下类型的输出。

输入:

id   startdate  enddate
1    21/01/2019 23/01/2019
1    23/01/2019 24/01/2019
1    24/01/2029 27/01/2019
1    29/01/2019 02/02/2019

输出:

id  startdate   enddate
1   21/01/2019  27/01/2019
1   29/01/2019  02/02/2019

我们需要使用匹配第一条记录enddate和第n条记录startdate的逻辑。

【问题讨论】:

  • 几个问题:[1] 你真的在使用 SQL Server 2008 吗? Microsoft 不再支持此产品... [2] 您自己已经尝试过什么?向我们展示您的尝试,以便我们更好地指导您。 [3] 为什么要添加标签common-table-expression?这是解决方案的要求吗?
  • 从输入和输出数据样本中问题很清楚,但您没有回答我之前评论中的任何个问题...
  • 我用的是SQL server 2016,没有条件只用CTE

标签: sql sql-server datetime sql-server-2016 gaps-and-islands


【解决方案1】:

这是一个间隙和孤岛问题,您希望将“相邻”日期组合在一起。这是使用窗口函数的一种方法:想法是将当前开始日期与“上一个”行的结束日期进行比较,并使用窗口总和来定义组:

select id, min(startdate) startdate, max(enddate) enddate
from (
    select t.*,
        sum(case when startdate = lag_enddate then 0 else 1 end) over(partition by id order by startdate) grp
    from (
        select t.*,
            lag(enddate) over(partition by id order by startdate) lag_enddate
        from mytable t
    ) t
) t
group by id, grp

Demo on DB Fiddle - 感谢 Sander 首先创建了 DDL 语句:

编号 |开始日期 |结束日期 -: | :--------- | :--------- 1 | 2019-01-21 | 2019-01-27 1 | 2019-01-29 | 2019-02-02

【讨论】:

    【解决方案2】:

    看看

    1. NEXT VALUE FOR 方法,适用于 2016 年及以后

    2. 使用 CTE 或子查询(在 2008 年有效),您可以在其中使用以前的值作为连接来连接您自己的表。这是我使用的示例脚本,用于显示备份增长

      declare @backupType char(1)
             , @DatabaseName sysname
      
       set @DatabaseName   = db_name() --> Name of current database, null for all databaseson server
       set @backupType     ='D'        /* valid options are:
                                       D = Database 
                                       I = Database Differential 
                                       L = Log 
                                       F = File or Filegroup 
                                       G = File Differential 
                                       P = Partial 
                                       Q = Partial Differential
                                       */
      
      
      
       select  backup_start_date
             , backup_finish_date
             , DurationSec
             , database_name,backup_size
             , PreviouseBackupSize
             , backup_size-PreviouseBackupSize as growth
             ,KbSec= format(KbSec,'N2')
       FROM (
       select backup_start_date 
            , backup_finish_date
            , datediff(second,backup_start_date,b.backup_finish_date) as DurationSec 
            , b.database_name
            , b.backup_size/1024./1024. as backup_size
      
            ,case when datediff(second,backup_start_date,b.backup_finish_date) >0 
               then ( b.backup_size/1024.)/datediff(second,backup_start_date,b.backup_finish_date)
               else 0 end as KbSec
       --   , b.compressed_backup_size
            , (      
               select top (1) p.backup_size/1024./1024. 
                 from msdb.dbo.backupset p 
                where p.database_name = b.database_name
                  and p.database_backup_lsn< b.database_backup_lsn
                   and type=@backupType
                order by p.database_backup_lsn desc
               ) as PreviouseBackupSize
       from msdb.dbo.backupset as b
       where @DatabaseName IS NULL OR database_name =@DatabaseName
       and type=@backupType
       )as A
       order by backup_start_date desc
      
    3. 使用“光标本地 fast_forward”逐行遍历数据,并使用临时表存储和比较上一个值

    【讨论】:

      【解决方案3】:

      这是一个可以使用公用表表达式的解决方案。

      样本数据

      create table data
      (
        id int,
        startdate date,
        enddate date
      );
      
      insert into data (id, startdate, enddate) values
      (1, '2019-01-21', '2019-01-23'),
      (1, '2019-01-23', '2019-01-24'),
      (1, '2019-01-24', '2019-01-27'),
      (1, '2019-01-29', '2019-02-02');
      

      解决方案

      -- determine start dates
      with cte_start as
      (
        select s.id,
               s.startdate
        from data s
        where not exists ( select 'x'
                           from data e
                           where e.id = s.id
                             and e.enddate = s.startdate )
      ),
      -- determine date boundaries
      cte_startnext as
      (
        select s.id,
               s.startdate,
               lead(s.startdate) over (partition by s.id order by s.startdate) as startdate_next
        from cte_start s
      )
      -- determine periods
      select sn.id,
             sn.startdate,
             e.enddate
      from cte_startnext sn
      cross apply ( select top 1 e.enddate
                    from data e
                    where e.id = sn.id
                      and  e.startdate >= sn.startdate
                      and (e.startdate <  sn.startdate_next or sn.startdate_next is null)
                    order by e.enddate desc ) e
      order by sn.id,
               sn.startdate;
      

      结果

      id startdate  enddate
      -- ---------- ----------
      1  2019-01-21 2019-01-27
      1  2019-01-29 2019-02-02
      

      Fiddle 查看解决方案和中间 CTE 结果的建立。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-05
        • 2021-08-05
        • 1970-01-01
        • 2023-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多