Sql 如何根据指定持续时间下的行之间的差异“分组”事件?

Sql 如何根据指定持续时间下的行之间的差异“分组”事件?,sql,sql-server-2012,Sql,Sql Server 2012,我遇到的问题是,如何将几个事件组合在一起。我仅有的一个指标是两个、三个事件之间的时间。一个人正在做一些任务开始/结束-14小时内的所有事情-被视为一个工作日。好吧,现在也超过午夜了,所以约会是没有选择的。 我已经建立了一个查询,它将在第一条记录中为我指明有多少条记录属于它。这是解决问题的一种方法 declare @MyTable table (UserID int, StartDate datetime, FinishDate datetime, GroupCount int); insert

我遇到的问题是,如何将几个事件组合在一起。我仅有的一个指标是两个、三个事件之间的时间。一个人正在做一些任务开始/结束-14小时内的所有事情-被视为一个工作日。好吧,现在也超过午夜了,所以约会是没有选择的。 我已经建立了一个查询,它将在第一条记录中为我指明有多少条记录属于它。这是解决问题的一种方法

declare @MyTable table
(UserID int, StartDate datetime, FinishDate datetime, GroupCount int);

insert into @MyTable values

('6', '2014-03-18 10:20:00.000', '2014-03-18 13:10:00.000', '2'), --(should take StartDate from this row - and Enddate from next (2) row)
('6', '2014-03-18 13:35:00.000', '2014-03-18 16:25:00.000', '1'),
('6', '2014-03-19 12:05:00.000', '2014-03-19 14:55:00.000', '1'),
('21', '2014-03-14 14:50:00.000', '2014-03-14 15:40:00.000', '1'),
('21', '2014-03-18 13:35:00.000', '2014-03-18 16:55:00.000', '1'),
('99', '2014-03-10 08:05:00.000', '2014-03-10 10:55:00.000', '2'),
('99', '2014-03-10 11:20:00.000', '2014-03-10 14:10:00.000', '1'),
('99', '2014-03-11 10:20:00.000', '2014-03-11 13:10:00.000', '2'),
('99', '2014-03-11 13:50:00.000', '2014-03-11 16:40:00.000', '1');
从@MyTable中选择*

我需要找到一种方法——以某种方式将它们组合在一起——这样我就有了最小开始日期和最大完成日期

最后,它应该是这样的:

declare @MyResult table
(UserID int, StartDate datetime, FinishDate datetime);

insert into @MyResult values

('6', '2014-03-18 10:20:00.000', '2014-03-18 16:25:00.000'),
('6', '2014-03-19 12:05:00.000', '2014-03-19 14:55:00.000'),
('21', '2014-03-14 14:50:00.000', '2014-03-14 15:40:00.000'),
('21', '2014-03-18 13:35:00.000', '2014-03-18 16:55:00.000'),
('99', '2014-03-10 08:05:00.000', '2014-03-10 14:10:00.000'),
('99', '2014-03-11 10:20:00.000', '2014-03-11 16:40:00.000');

select UserID, StartDate, Finishdate, datediff (minute, StartDate, FinishDate) as Duration,
LEAD(startdate,1,NULL) over(partition by userid order by startdate) NextDuty,
  DATEDIFF(minute,FinishDate,LEAD(StartDate,1,NULL) over(partition by userid order by StartDate)) as DifMin

   from @MyResult
这也取决于用户ID。群组计数-只是一个想法。。。但我不知道如何跳转2条记录-选择下一个开始-GroupCount字段等。 2将指示-当前记录和下一个记录属于一起,1仅此实际记录。 也会有3或4个记录属于一起。
所有这些都应该在MS-SQL 2012中完成。

不幸的是,您的情况似乎需要遍历数据,一次遍历一行。如果条件是在至少有8小时间隔的情况下开始新的一行,那么还有其他一些可能性。但是,逻辑必须从每个客户的第一行开始,分配组,然后在确定14小时内的所有任务后,使用逻辑增加组

以下方法使用递归CTE。我能想到的唯一其他SQL替代方法是游标

with t as (
      select t.*,
             row_number() over (partition by UserId order by StartDate) as seqnum
      from MyTable t
     ),
     cte as (
      select t.UserId, t.StartDate, t.FinishDate, seqnum,
             1 as grp, t.StartDate as grp_start
      from t
      where seqnum = 1
      union all
      select t.UserId, t.StartDate, t.FinishDate, t.seqnum,
             (case when t.StartDate - cte.grp_start <= 14.0/24
                   then cte.grp
                   else cte.grp + 1
              end),
             (case when t.StartDate - cte.grp_start <= 14.0/24
                   then cte.grp_start
                   else t.StartDate
              end)
      from cte join
           t
           on cte.UserId = t.UserId and
              cte.seqnum = t.seqnum - 1
     )
select userid, min(startdate), max(finishdate)
from cte
group by userid, grp      
order by 1, 2;

你可以看到这项工作。

我知道这个问题由来已久,但有更好的方法。鉴于您的问题比标准岛稍微复杂,我可以提供:

select t.UserID
    , t.StartDate, isnull(b.FinishDate, t.FinishDate) as FinishDate
    , datediff(minute, t.StartDate, isnull(b.FinishDate, t.FinishDate)) as Duration
    , n.NextDuty
    , datediff(minute, isnull(b.FinishDate, t.FinishDate), n.NextDuty) as DiffMin
from @MyTable t
    outer apply (
        select top 1 FinishDate
        from @MyTable b
        where b.UserID = t.UserID
            and b.StartDate > t.StartDate
            and datediff(hh, t.StartDate, b.StartDate) < 14
        order by b.StartDate desc
    ) b
    outer apply (
        select top 1 StartDate as NextDuty
        from @MyTable n
        where n.UserID = t.UserID
            and n.StartDate > t.StartDate
            and datediff(hh, t.StartDate, n.StartDate) > 14
        order by n.StartDate
    ) n
where not exists (
        select top 1 1
        from @MyTable p
        where p.UserID = t.UserID
            and p.StartDate < t.StartDate
            and datediff(hh, p.StartDate, t.StartDate) < 14
    )
在实际表中,您需要确保此索引已就位:

CREATE INDEX IX_nameThisIndex ON <@MyTable> (UserId, StartDate, FinishDate)
这应该会得到可靠的结果,我已经用其他数据进行了测试,但还没有用大数据集进行过详尽的测试。大型集合将需要索引


希望这能有所帮助。

谢谢戈登的回答。现在我遇到了问题-只要我有100多条记录。。。我的声明被终止了。在语句完成之前,已耗尽最大递归100。有什么解决方法吗?好吧-使用OptionMaxRecursion 0,没有更多限制。。。它只是慢了很多-获得8000条记录的结果-700条结果-大约需要10秒但它是有效的