根据另一张表将一条记录分成多个

时间:2019-02-18 03:01:10

标签: sql sql-server

我有一张桌子追踪住处。有一个ID,开始日期时间,结束日期时间和其他字段。

我有另一个表,其中包含在每个逗留期间发生的事件,具有相似的开始和结束时间,并且链接在ID字段上。

我需要做的是将两者合并,然后将位置表拆分为各个事件。这里的窍门是某个地点可能开始于2017-08-02,但第一场活动可能几天都不会开始。因此,我一开始就需要记录该差距。

样本数据

 CREATE TABLE #Stays (
 EpID INT, StayId INT, StayStartDate DateTime, StayEndDate DateTime);
CREATE TABLE #Events (
 EpID INT, EventId INT, EventStartDate DateTime, EventEndDate DateTime, EventNumber INT);

INSERT INTO #Events SELECT   1, 7897, '2016-11-24 00:00:00.000','2016-11-26 00:00:00.000', 1
INSERT INTO #Events SELECT   1, 7898, '2016-11-26 00:00:00.000','2016-11-28 00:00:00.000', 2
INSERT INTO #Stays  SELECT   1, 10,   '2016-11-22 08:15:00.000','2016-11-24 10:54:00.000' 
INSERT INTO #Stays  SELECT   1, 11,   '2016-11-24 10:54:00.000','2016-11-24 11:17:00.000' 
INSERT INTO #Stays  SELECT   1, 12,   '2016-11-24 11:17:00.000','2016-11-25 08:16:00.000' 
INSERT INTO #Stays  SELECT   1, 13,   '2016-11-25 08:16:00.000','2016-11-28 23:15:00.000' 

预期输出为

EpId    StartDate                 EndDate          EventNumber
1   2016-11-22 08:15:00.000 2016-11-23 23:59:59.000 NULL
1   2016-11-24 00:00:00.000 2016-11-25 23:59:59.000 7897
1   2016-11-26 00:00:00.000 2016-11-27 23:59:59.000 7898
1   2016-11-28 00:00:00.000 2016-11-28 23:15:00.000 NULL

这是我正在尝试的。它当前无法正常工作,我确定我正在使用的方法可能不是最佳方法。当前它没有将两个数据集融合在一起。 我的猜测是,通过外部或交叉应用可以更轻松地完成此操作,但是我对它们如何工作的了解非常有限。 有帮助吗?

;with e as (
SELECT [EpID]
      ,EventId
      ,[EventNumber]
      ,case when [EventStartDate] > DayStart then [EventStartDate] else DayStart end as [EventStart]
      ,case when [EventEndDate] < DayEnd then [EventEndDate] else DayEnd end as [EventEnd]
  FROM [Events] e
  inner join DimStaySegmentDayReference d on d.DayEnd >= e.[EventStartDate] and d.DayStart <= e.[EventEndDate]
),
s as (
  select 
    [EpID]
    ,StayId
     ,case when StayStartDate > DayStart then StayStartDate else DayStart end as [StayStart]
      ,case when StayEndDate < DayEnd then StayEndDate else DayEnd end as [StayEnd]

  from  Stays s
  inner join  DimStaySegmentDayReference d on d.DayEnd >= StayStartDate and d.DayStart <= StayEndDate
),

  u as (select 'stay' as source, [EpID],  StayStart, StayEnd, '' as event from s 
union all
select 'event' as source, [EpID],  [EventStart], [EventEnd], eventnumber as event  from e)

select  Source, 
    [EpID], 
    Staystart,
    stayend, 
    case when lag(stayend) over (partition by EpId ORDER BY STAYSTART) < StayEnd-0.0001 AND source='event' then lag(stayend) over (partition by EpId ORDER BY STAYSTART) else staystart end as staystartnew,
    case when lead(staystart) over (partition by EpID ORDER BY StayStart) < stayend then lead(staystart) over (partition by EpID ORDER BY StayStart) else stayend end as stayendnew,

    event
    from u 
    where StayStart <> stayend
order by StayStart

DayReference表只是每天都有开始时间和结束时间,因此我可以将记录分为天段。

我正在使用SQL Server 2012

编辑某些上下文 我已经更新了样本数据,使其更加清晰。 停留表可跟踪位置停留。在这种情况下,我将忽略多个位置以使查找解决方案变得更加容易。 位置和事件彼此不可知,除了在同一时间范围内针对同一EpID发生之外。
例如,考虑跟踪工作时间,您从上午9点开始,到下午5点结束。对于这个工作日,您将说5个位置在组成完整的班次。 9-11桌,11-12会议,12-1午餐,1-3会议,3-5桌。 然后,您将发生一系列事件,可以称其为喝咖啡。您在9:30到10之间以及2-4之间喝咖啡。
我需要做的是将这两组数据网格在一起以创建单个时间轴。 9-930桌,930-10咖啡,10-11桌,11-12会议,12-1午餐,1-2会议,2-4咖啡,4-5桌。 希望这会有所帮助

1 个答案:

答案 0 :(得分:0)

可能有些事情可以简化,但是很容易理解我针对每种情况验证的内容,而且,我认为您的输出示例中缺少一行,我从2018-09-14获得了最后一行16 :00至2018-09-15 12:00,我在逻辑或问题上找不到丢弃它的原因

需要额外的验证,并且不需要任何注册事件即可直接加入Stays,但这是我的方法

;WITH CTE AS (
    SELECT D.*, s.StayId, 
    EventNumber,
    LAG(D.DStart) OVER (ORDER BY EventNumber) As LagStart,
    LAG(StayID) OVER (ORDER BY EventNumber) As LagStay,
    LAG(Event) OVER (ORDER BY EventNumber) As LagEvent,
    LEAD(D.DEnd) OVER (ORDER BY EventNumber) As LeadEnd,
    LEAD(StayID) OVER (ORDER BY EventNumber) As LeadStay,
    LEAD(Event) OVER (ORDER BY EventNumber) As LeadEvent
    FROM #Events E
    CROSS APPLY
    (
        SELECT TOP 1 * FROM #Stays S WHERE E.EventStartDate BETWEEN S.StayStartDate AND S.StayEndDate
        UNION
        SELECT TOP 1 * FROM #Stays S WHERE E.EventEndDate BETWEEN S.StayStartDate AND S.StayEndDate
    ) S
    CROSS APPLY (
        SELECT StayStartDate AS DStart, EventStartDate DEnd, Null AS Event, 1 as c WHERE StayStartDate < EventStartDate
        UNION
        SELECT EventStartDate, EventEndDate, EventNumber, 2 WHERE EventStartDate >= StayStartDate AND EventEndDate <= StayEndDate
        UNION
        SELECT StayStartDate, EventEndDate, EventNumber, 3 WHERE StayStartDate > EventStartDate AND EventEndDate < StayEndDate
        UNION
        SELECT EventStartDate, StayEndDate, EventNumber, 4 WHERE StayStartDate < EventStartDate AND EventEndDate > StayEndDate
        UNION
        SELECT EventEndDate, StayEndDate, Null, 5 WHERE EventEndDate < StayEndDate
    ) D
)
SELECT DISTINCT 
CASE WHEN LagStay = StayId AND Event IS NULL AND LagEvent IS NULL THEN LagStart 
ELSE DStart END AS StartDate,
CASE WHEN LeadStay = StayId AND Event IS NULL AND LeadEvent IS NULL THEN LeadEnd 
ELSE DEnd END AS EndDate,
Event, StayID
FROM CTE
ORDER BY StartDate