SQL to find time elapsed from multiple overlapping intervals

后端 未结 3 1908
-上瘾入骨i
-上瘾入骨i 2020-12-05 20:50

Not using MSSQL or DB2 or Oracle. No CTE. No OVERLAP predicate. No INTERVAL data type. The situation: on a vehicle to be repaired work can not start until all parts orde

3条回答
  •  难免孤独
    2020-12-05 21:39

    This SQL statement seems to get what you want (t is the table name of the sampe table):

    SELECT
       d.id, 
       d.duration, 
       d.duration - 
       IFNULL(
          ( SELECT Sum( timestampdiff( SQL_TSI_DAY, 
                                       no_hold.d2, 
                                       ( SELECT min(d1) FROM t t4 
                                         WHERE t4.id = no_hold.id and t4.d1 > no_hold.d2 )))
            FROM ( SELECT DISTINCT id, d2 FROM t t1 
                   WHERE ( SELECT sum( IIF( t1.d2 between t2.d1 and t2.d2, 1, 0 ) ) 
                           FROM t t2 WHERE t2.id = t1.id and t2.d2 <> t1.d2 ) = 0 
                 And d2 <> ( select max( d2 ) from t t3 where t3.id = t1.id )) no_hold
            WHERE no_hold.id = d.id ),
          0 ) "parts hold"
    FROM 
       ( SELECT id, timestampdiff( SQL_TSI_DAY, min( d1 ), max( d2 ) ) duration
         FROM t GROUP BY id ) d
    

    The outer query gets the duration of the repair work. The complex subquery calculates the total number of days not waiting for parts. This is done by locating the start dates where the vehicle is not waiting for parts, and then count the number of days until it begins to wait for parts again:

    // 1) The query for finding the starting dates when the vehicle is not waiting for parts, 
    // i.e. finding all d2 that is not within any date range where the vehicle is waiting for part.
    // The DISTINCT is needed to removed duplicate starting "no hold" period.
    
    SELECT DISTINCT id, d2 
    FROM t t1
    WHERE ( SELECT sum( IIF( t1.d2 between t2.d1 and t2.d2, 1, 0 ) ) from t t2 
            WHERE t2.id = t1.id and t2.d2 <> t1.d2 ) = 0 AND 
          d2 <> ( SELECT max( d2 ) FROM t t3 WHERE t3.id = t1.id ) )
    

    // 2) The days where it vehicle is not waiting for part is the date from the above query till the vehicle is // waiting for part again

    timestampdiff( SQL_TSI_DAY, no_hold.d2, ( SELECT min(d1) FROM t t4 WHERE t4.id = no_hold.id and t4.d1 > no_hold.d2 ) )
    

    Combining the two above and aggregating all such periods gives the number of days that the vehicle is not waiting for parts. The final query adds an extra condition to calculate result for each id from the outer query.

    This probably is not terribly efficient on very large table with many ids. It should fine if the id is limited to one or just a few.

提交回复
热议问题