【问题标题】:SQL Server Query to group sequential datesSQL Server 查询以对连续日期进行分组
【发布时间】:2012-09-11 13:09:12
【问题描述】:

我有一个名为 Absence Details 的表,我想对连续日期进行分组。这是数据

EID        AbsenceType  AbsenceStartDate               AbsenceEndDate
769     Holiday     2012-06-25  00:00:00.000            2012-06-25 23:59:59.000
769     Holiday     2012-06-26  00:00:00.000            2012-06-26 23:59:59.000
769     Holiday     2012-09-03  00:00:00.000            2012-09-03 23:59:59.000
769     Holiday     2012-09-04  00:00:00.000            2012-09-04 23:59:59.000
769     Holiday     2012-09-05  00:00:00.000            2012-09-05 23:59:59.000
769     Holiday     2012-09-06  00:00:00.000            2012-09-06 23:59:59.000
769     Holiday     2012-09-07  00:00:00.000            2012-09-07 23:59:59.000

我想要得到的结果是

EID     AbsenceType AbsenceStartDate          AbsenceEndDate
769     Holiday     2012-06-25  00:00:00.000         2012-06-26 23:59:59.000
769     Holiday     2012-09-03  00:00:00.000         2012-09-07 23:59:59.000

非常感谢任何帮助。

【问题讨论】:

  • 您忘记从标题中删除 AbsenceType 列或为此列添加值。
  • @ Vikdor - 这是真的,我忘了删除缺席原因列。谢谢你。
  • @podiluska - 我还没有开始编写任何查询。只是寻找指针。
  • 搜索“岛屿和峡谷”

标签: sql tsql sql-server-2008-r2


【解决方案1】:

我已简化您的方案以隔离主要问题。让我们假设这个表有间隙:

with ns as (
select 1 as n union
select 2 as n union
select 3 as n union
select 8 as n union    --gap
select 9 as n )
select * 
into #ns
from ns;

现在,您期望的结果是:

ini fi 
--- -- 
1   3  
8   9  

为了得到这个结果,我以这种方式处理数据:首先,我创建两个带有开始和结束时间段的视图,其次,我加入两个视图以获得最终结果。请注意,我用它自己加入表格来定位开始和结束时期:

with 
inis as                                     -- identifying start periods
(
   select n1.n
   from #ns n1
   left outer join #ns n2 
       on n1.n = n2.n + 1
   where n2.n is null
   ),
fis as                                      -- identifying ends periods
( 
   select n1.n 
   from #ns n1
   left outer join #ns n2 
       on n1.n = n2.n - 1
   where n2.n is null
   )  
select inis.n as ini, min( fis.n ) as fi    -- joining starts and ends
from inis 
inner join fis 
  on inis.n <= fis.n
group by inis.n
;
   

您可以将此技术转移到您的数据和数据类型中。如果您在翻译查询时遇到任何问题,请随时提出。

Check query and results.

【讨论】:

    【解决方案2】:

    这是对我有用的解决方案。

    SELECT EID, AbsenceType, MIN(AbsenceStartDate) AS AbsenceStartDate, MAX(AbsenceEndDate) AS AbsenceEndDate
    FROM (SELECT EID, AbsenceType, AbsenceStartDate, AbsenceEndDate,
          DATEADD(dd, - ROW_NUMBER() OVER (PARTITION BY EID, AbsenceType ORDER BY EID,AbsenceStartDate), AbsenceStartDate)
          FROM AbsenceDetails
          GROUP BY EID,AbsenceType,AbsenceStartDate,AbsenceEndDate
          ) a(EID, AbsenceType, AbsenceStartDate, AbsenceEndDate, Grp)
    GROUP BY EID, AbsenceType, Grp;
    

    【讨论】:

    • +1 这是一个很好的答案。下次记得格式化代码
    【解决方案3】:

    我会这样做:

    1. 确定缺勤日期的顺序列表。

      SELECT
          ad1.EID, ad1.StartDate, ad2.EndDate
      FROM 
          AbsenceDetails ad1
          JOIN AbsenceDetails ad2
          ON ad1.EID = ad2.EID
      WHERE
          DATEDIFF(ss, ad1.EndDate, ad2.StartDate) = 1
      

      结果如下:

      769 2012-06-25 00:00:00.000 2012-06-26 23:59:59.000
      769 2012-09-03 00:00:00.000 2012-09-04 23:59:59.000
      769 2012-09-04 00:00:00.000 2012-09-05 23:59:59.000
      769 2012-09-05 00:00:00.000 2012-09-06 23:59:59.000
      769 2012-09-06 00:00:00.000 2012-09-07 23:59:59.000
      
    2. 遍历列表并确定每个拉伸的开始和结束持续时间。这最好在应用层完成。

    【讨论】:

      【解决方案4】:

      如果我正确理解了您的问题,您希望在记录中找到连续的时间间隔。
      主要问题将是确定实际构成连续时间间隔的内容:
      如果您正在查看工作中的缺勤,而不是任何序列

      date1.09:00 to date1.18:00  
      date2.09:00 to date2.18:00  
      

      其中date2 是date1 之后的下一个工作日,可以认为是连续的。

      在您的情况下,这相对容易,但您无法在单个查询中完成。至少我现在想不出办法。

      附: “podiluska”建议的“Islands and Gaps”算法将帮助您在单个查询/存储过程中编写它。

      【讨论】:

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