T-SQL Start and end date times from a single column

后端 未结 3 1522
夕颜
夕颜 2021-01-24 11:34

I have a single table of on/off events for different assets. I need to get a list of start & stop event times without using a cursor.

Source:

Item          


        
相关标签:
3条回答
  • 2021-01-24 11:42
    declare @YourTable table (Item varchar(10), EventDate datetime, Event varchar(10)) 
    
    insert into @YourTable values 
    ('A','2011-10-03 00:01:00''On'), 
    ('B','2011-10-03 00:01:00','On'), 
    ('A','2011-10-03 00:02:00','Off'), 
    ('C','2011-10-03 00:01:00','On'), 
    ('B','2011-10-03 00:02:00','Off'), 
    ('A','2011-10-03 00:02:02','On'),   
    ('C','2011-10-03 00:02:05','Off'), 
    ('A','2011-10-03 00:02:07','Off'), 
    ('D','2011-10-03 00:02:02','On')   
    
    ;with a as
    (
    select item, eventdate, event, ROW_NUMBER() Over(Partition by Item order by EventDate) RN 
    from @YourTable
    ),
    b as (
    select item, eventdate, 0 e, rn, event from a where rn = 1
    union all
    select b.item, a.eventdate, case when b.event = 'Off' then b.e+1 else b.e end, a.rn, a.event
    from b 
    join a on b.rn + 1 = a.rn and a.item = b.item
    )
    select item, min(eventdate) Start, max(eventdate) [End]
     from b
    group by item, e
    order by item, e
    
    0 讨论(0)
  • 2021-01-24 11:50

    Untested...

    ;WITH myCTE AS
    (
       SELECT *,
             ROW_NUMBER() OVER (PARTITION BY Item ORDER BY EventDate) AS rn
       FROM MyTable
    )
    SELECT
       M1.Item,
       M1.EventDate AS Start,
       M2.EventDate AS End
    FROM
       myCTE M1
       JOIN
       myCTE M2 ON M1.Item = M2.Item AND M1.rn+1 = M2.rn
    WHERE
       M1.Event = 'On'
       AND
       M2.Event = 'Off';
    
    0 讨论(0)
  • 2021-01-24 11:55

    Here's the solution, with proof of concept for everyone to verify.

    I noticed that the Off event for C was marked as a new On. I fixed that, but that also led me to coding a solution that would allow having an event that had started but not finished, so I included an open ended event D.

    Additionally, my solution works with overlapping periods.

    declare @YourTable table (Item varchar(10), 
                              EventDate datetime, 
                              Event varchar(10))
    
    insert into @YourTable values
    ('A',         '2011-10-03 00:01:00',     'On'),
    ('B',         '2011-10-03 00:01:00',     'On'),
    ('A',         '2011-10-03 00:02:00',     'Off'),
    ('C',         '2011-10-03 00:01:00',     'On'),
    ('B',         '2011-10-03 00:02:00',     'Off'),
    ('A',         '2011-10-03 00:02:02',     'On'),  
    ('C',         '2011-10-03 00:02:05',     'Off'),
    ('A',         '2011-10-03 00:02:07',     'Off'),
    ('D',         '2011-10-03 00:02:02',     'On')  
    
    select tOn.Item, tOn.EventDate Start, tOff.EventDate [End]
    from (
    select Item, EventDate, 
           ROW_NUMBER() Over(Partition by Item order by EventDate) EventID
    from @YourTable where Event = 'On'
    ) tOn
    LEFT JOIN (
    select Item, EventDate, 
           ROW_NUMBER() Over(Partition by Item order by EventDate) EventID
    from @YourTable where Event = 'Off'
    ) tOff
    on (tOn.Item = tOff.Item and tOn.EventID = tOff.EventID)
    

    Explained

    We divide the data set in 2: On events and Off events. Each containing a numbered row which restarts when Item changes.

    Basically, we have a first in first out: The first On will be closed by the first Off, so overlapping periods will be supported by this query given this approach. So each On Event for A will have its Event ID, which will be linked to the correspondent Off Event ID.

    Open ended periods will be supported by the LEFT JOIN.

    0 讨论(0)
提交回复
热议问题