WITH base AS ( SELECT t_stamp, Name, Full, Half_Full, Jam, No_Container, Block_Operation FROM lane_data WHERE t_stamp BETWEEN :startTime AND :endTime ), -- Unpivot signals into rows ev AS ( SELECT Name, t_stamp, 'Full' AS sig, Full AS val FROM base UNION ALL SELECT Name, t_stamp, 'Half_Full', Half_Full FROM base UNION ALL SELECT Name, t_stamp, 'Jam', Jam FROM base UNION ALL SELECT Name, t_stamp, 'No_Container', No_Container FROM base UNION ALL SELECT Name, t_stamp, 'Block_Operation', Block_Operation FROM base ), -- Add previous and next values, and find falling edges ch AS ( SELECT Name, sig, t_stamp, val, LAG(val) OVER (PARTITION BY Name, sig ORDER BY t_stamp) AS prev_val, LEAD(val) OVER (PARTITION BY Name, sig ORDER BY t_stamp) AS next_val, LEAD(t_stamp) OVER (PARTITION BY Name, sig ORDER BY t_stamp) AS next_ts FROM ev ), -- Mark rising and falling edges edges AS ( SELECT Name, sig, t_stamp, val, CASE WHEN val = 1 AND (prev_val = 0 OR prev_val IS NULL) THEN 1 -- rising edge ELSE 0 END AS is_rising, CASE WHEN val = 0 AND prev_val = 1 THEN 1 -- falling edge ELSE 0 END AS is_falling FROM ch ), -- Find interval starts (rising edges) starts AS ( SELECT Name, sig, t_stamp AS start_ts FROM edges WHERE is_rising = 1 ), -- Find interval ends (falling edges) ends AS ( SELECT Name, sig, t_stamp AS end_ts FROM edges WHERE is_falling = 1 ), -- Match each start with the next falling edge (or endTime if none exists) intervals AS ( SELECT s.Name, s.sig, s.start_ts, COALESCE( (SELECT MIN(e.end_ts) FROM ends e WHERE e.Name = s.Name AND e.sig = s.sig AND e.end_ts > s.start_ts), :endTime ) AS end_ts FROM starts s ), -- Sum durations dur AS ( SELECT Name, SUM(CASE WHEN sig = 'Full' THEN TIMESTAMPDIFF(SECOND, start_ts, end_ts) ELSE 0 END) AS Full_Duration_Sec, SUM(CASE WHEN sig = 'Half_Full' THEN TIMESTAMPDIFF(SECOND, start_ts, end_ts) ELSE 0 END) AS Half_Full_Duration_Sec, SUM(CASE WHEN sig = 'Jam' THEN TIMESTAMPDIFF(SECOND, start_ts, end_ts) ELSE 0 END) AS Jam_Duration_Sec, SUM(CASE WHEN sig = 'No_Container' THEN TIMESTAMPDIFF(SECOND, start_ts, end_ts) ELSE 0 END) AS No_Container_Duration_Sec, SUM(CASE WHEN sig = 'Block_Operation' THEN TIMESTAMPDIFF(SECOND, start_ts, end_ts) ELSE 0 END) AS Block_Operation_Duration_Sec FROM intervals GROUP BY Name ) SELECT :startTime AS `Start Time`, :endTime AS `End Time`, d.Name AS `Lane ID`, CASE WHEN d.Name LIKE 'S01%' THEN 'S01' WHEN d.Name LIKE 'S02%' THEN 'S02' ELSE 'N/A' END AS `Sorter`, COALESCE(Full_Duration_Sec, 0) AS `Full Duration Sec`, COALESCE(Half_Full_Duration_Sec, 0) AS `Half Full Duration Sec`, COALESCE(Jam_Duration_Sec, 0) AS `Jam Duration Sec`, COALESCE(No_Container_Duration_Sec, 0) AS `No Container Duration Sec`, COALESCE(Block_Operation_Duration_Sec, 0) AS `Block Operation Duration Sec` FROM dur d UNION ALL SELECT :startTime, :endTime, 'N/A', 'N/A', 0,0,0,0,0 WHERE NOT EXISTS (SELECT 1 FROM dur) ORDER BY `Lane ID`;