在数据序列中选择“边缘”记录

rob*_*rtc 6 sql-server-2005 sql-server-2008 t-sql gaps-and-islands

我有一个按日期顺序运行的项目表,每个条目都有开始和结束:

ID | Start            | End
----------------------------------------
1  | 2012-08-20 00:00 | 2012-08-20 23:59 
2  | 2012-08-21 00:00 | 2012-08-21 23:59 
3  | 2012-08-22 00:00 | 2012-08-22 23:59 
4  | 2012-08-23 00:00 | 2012-08-23 23:59 
5  | 2012-08-24 00:00 | 2012-08-24 23:59 
6  | 2012-08-28 00:00 | 2012-08-28 23:59 
7  | 2012-08-29 00:00 | 2012-08-29 23:59 
8  | 2012-08-30 00:00 | 2012-08-30 23:59
Run Code Online (Sandbox Code Playgroud)

请注意,数据中存在差距(周末和银行假期)。是否可以编写一个查询来选择间隙边缘的记录?我想要的是一种通用的方式来结束这样的事情:

ID | Start            | End
----------------------------------------
1  | 2012-08-20 09:00 | 2012-08-20 23:59 
2  | 2012-08-21 00:00 | 2012-08-21 23:59 
3  | 2012-08-22 00:00 | 2012-08-22 23:59 
4  | 2012-08-23 00:00 | 2012-08-23 23:59 
5  | 2012-08-24 00:00 | 2012-08-24 17:00 
6  | 2012-08-28 09:00 | 2012-08-28 23:59 
7  | 2012-08-29 00:00 | 2012-08-29 23:59 
8  | 2012-08-30 00:00 | 2012-08-30 17:00
Run Code Online (Sandbox Code Playgroud)

请注意,1 和 6 的开始时间已更新,5 和 8 的结束时间也已更新。

在真实的数据库中,我不得不每天使用这种数据布局,因为它必须保持与旧客户端的兼容性,因此我不能简单地直接插入范围。同样在真实数据中,我可能一次处理数百条这样的记录,但存在多个间隙。

目标平台是 SQL2005/2008,不过在 SQL2000 兼容模式下工作的解决方案会获得加分。

Mik*_*son 9

您可以使用outer apply获取leadlag值,并在列列表datediff中查看差距是否不是一天。

select T1.ID,
       case
         when datediff(day, Lag.Start, T1.Start) = 1 then T1.Start
         else dateadd(hour, 9, T1.Start)
       end as Start,
       case
         when datediff(day, T1.Start, Lead.Start) = 1 then T1.[End]
         else dateadd(hour, 17, T1.Start)
       end as [End]
from YourTable as T1
  outer apply
    (
      select top(1) T2.Start
      from YourTable as T2
      where T2.Start < T1.Start
      order by T2.Start desc
    ) as Lag(Start)
  outer apply
    (
      select top(1) T2.Start
      from YourTable as T2
      where T2.Start > T1.Start
      order by T2.Start
    ) as Lead(Start)
Run Code Online (Sandbox Code Playgroud)

即使与 SQL Server 2000 (80) 兼容,上述查询也适用于 SQL Server 2005。

如果您找到了使用 SQL Server 2012 的方法,您可以使用超前滞后而不是使用outer apply.

select T1.ID,
       case
         when datediff(day, lag(T1.Start) over(order by T1.Start), T1.Start) = 1 
         then T1.Start
         else dateadd(hour, 9, T1.Start)
       end as Start,
       case
         when datediff(day, T1.Start, lead(T1.Start) over(order by T1.Start)) = 1 
         then T1.[End]
         else dateadd(hour, 17, T1.Start)
       end as [End]
from YourTable as T1
Run Code Online (Sandbox Code Playgroud)

SQL小提琴