简体   繁体   中英

SQL - Start and End date based on another column

Simplified structure.

I need the two dates between a record that has an action type of 4 and an action type of 1.

The record could be in that state multiple times and I would need separate rows for their times

For example for IncidentId = 1

  • Row 1 - StartTime = 2017-01-01 14:00 (id:3) - End Time = 2017-01-01 20:00 (id: 5)
  • Row 2 - StartTime = 2017-01-01 21:00 (id:6) - End Time = 2017-01-02 11:00 (id: 9)
CREATE TABLE #returntable 
      ( 
         [incidentid] INT, 
         [starttime]  DATETIME, 
         [endtime]    DATETIME 
      ) 

    CREATE TABLE #testtableofdoom 
      ( 
         [incidentlogid] INT, 
         [incidentid]    INT, 
         [timestamp]     DATETIME, 
         [actiontypeid]  INT 
      ) 

     INSERT INTO #testtableofdoom
            ( incidentlogid, incidentid, timestamp, actiontypeid )
     VALUES ( 1, 1, '2017-01-01 09:00', 1 )
        ,   ( 2, 1, '2017-01-01 11:00', 1 )
        ,   ( 3, 1, '2017-01-01 14:00', 4 )
        ,   ( 4, 1, '2017-01-01 16:00', 4 )
        ,   ( 5, 1, '2017-01-01 20:00', 1 )
        ,   ( 6, 1, '2017-01-01 21:00', 4 )
        ,   ( 7, 1, '2017-01-02 09:00', 4 )
        ,   ( 8, 2, '2017-01-02 10:00', 1 )
        ,   ( 9, 1, '2017-01-02 11:00', 1 )
        ,   ( 10, 1, '2017-01-02 14:00', 1 )
        ,   ( 11, 2, '2017-01-02 15:00', 4 )
        ,   ( 12, 1, '2017-01-02 16:00', 1 )    
        ,   ( 13, 1, '2017-01-02 17:00', 1 )
        ,   ( 14, 1, '2017-01-02 18:00', 1 )
        ,   ( 15, 2, '2017-01-02 15:00', 1 );

    DROP TABLE #testtableofdoom 

    DROP TABLE #returntable

I used table variables instead of temp tables, and shorter column names than you, but this works:

declare @tt TABLE ( 
     logId INT, iId INT, 
     dt DATETIME, atId  INT  

INSERT @tt (logId, iId, 
    dt, atId) values 
(1, 1, '2017-01-01 09:00', 1),
(2, 1, '2017-01-01 11:00', 1),
(3, 1, '2017-01-01 14:00', 4),
(4, 1, '2017-01-01 16:00', 4),
(5, 1, '2017-01-01 20:00', 1),
(6, 1, '2017-01-01 21:00', 4),
(7, 1, '2017-01-02 09:00', 4),
(8, 2, '2017-01-02 10:00', 1),
(9, 1, '2017-01-02 11:00', 1),
(10, 1, '2017-01-02 14:00', 1),
(11, 2, '2017-01-02 15:00', 4),
(12, 1, '2017-01-02 16:00', 1),
(13, 1, '2017-01-02 17:00', 1),
(14, 1, '2017-01-02 18:00', 1),
(15, 2, '2017-01-02 15:00', 1)

Select s.logId startLogid, e.logId endLogId, 
       s.iID, s.dt startTime, e.dt endTime 
from @tt s join @tt e
    on e.logId = 
       (Select min(logId) from @tt
        where iId = s.iID
            and atId = 1
            and logId > s.logId)                
where s.aTid = 4 
      and ((Select atId from @tt
            Where logId = 
               (Select Max(logId) from @tt
                where logId < s.LogId 
                 and iId = s.iId)) = 1
        or Not Exists 
           (Select * from @tt
            Where logId < s.LogId 
              and iId = s.iID))    

This produces the following:

 startLogid  endLogId    iID  startTime               endTime
 ----------- ----------- ---- ----------------   ----------------
     3           5        1   2017-01-01 14:00   2017-01-01 20:00
     6           9        1   2017-01-01 21:00   2017-01-02 11:00
    11          15        2   2017-01-02 15:00   2017-01-02 15:00

it uses a self-join. s represents the first (start) record with actionType 4, and e represents end record with action type 1. Since logId increments, the end record must have higher logId than the start record, and it must be the lowest logId higher than the start records that has same iId and an atId = 1.

Select s.iID, s.dt startTime, e.dt endTime 
from @tt s join @tt e
    on e.logId = 
       (Select min(logId) from @tt    -- lowest log greater than start logId
        where iId = s.iID         -- same iId
            and atId = 1          -- with atId = 1
            and logId > s.logId) -- greater than start logId

finally, the start record must be restricted to those "4" records which either have no other same incident records before it or have a "1" record immediately prior to it.

where s.aTid = 4 
      and ((Select atId from @tt            -- atId of immed prior = 1
            Where logId = 
               (Select Max(logId) from @tt
                where logId < s.LogId 
                 and iId = s.iId)) = 1
        or Not Exists                       -- or there is no prior record
           (Select * from @tt
            Where logId < s.LogId 
              and iId = s.iID))  

something like this?

select 
d.[timestamp] as StartDate, 
(select top 1 [timestamp] 
 from #testTableOfDoom d2 
 where d2.incidentid = 1 and d2.[timestamp] > d.[timestamp] and actiontypeid = 1 
 order by d2.[timestamp] asc
) as EndDate
from
    (select 
    p.[timestamp],
    LAG(p.actiontypeid) OVER (ORDER BY incidentlogid asc) PrevValue,
    p.actiontypeid 
    from #testTableOfDoom p
    where p.incidentid = 1) d
where d.actiontypeid = 4 
and d.PrevValue <> 4

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM