Search code examples
postgresqltime-series

SQL timeseriees - Calculate duration of multiple device states per device


I'm trying to calculate the duration a list of devices are in a specific state for each state. Each state will not repeat for that device in the list.

I have it working for 1 device, but can't get the group / order by to work to process all the devices and return a set of rows per device.

example data

CREATE TABLE telemetry (device character varying ,time timestamptz, state int);
INSERT INTO telemetry (device, time, state) values

( 'Device_001', '2021-07-03 11:28:50',  3),
( 'Device_001', '2021-07-03 11:28:56',  0),
( 'Device_001', '2021-07-03 11:29:01',  1),
( 'Device_001', '2021-07-03 11:45:22',  0),
( 'Device_001', '2021-07-03 11:45:43',  3),
( 'Device_001', '2021-07-03 11:45:53',  1),
( 'Device_001', '2021-07-03 13:00:48',  0),
( 'Device_002', '2021-07-03 11:28:41',  3),
( 'Device_002', '2021-07-03 11:28:46',  0),
( 'Device_002', '2021-07-03 11:28:51',  3),
( 'Device_002', '2021-07-03 11:28:56',  0),
( 'Device_002', '2021-07-03 11:29:01',  1),
( 'Device_002', '2021-07-03 11:29:20',  3),
( 'Device_002', '2021-07-03 11:29:26',  0),

desired result:

deviceid    total   state1  state2  state3  state4
Device_001  5518    26  5476    0   16
Device_002  5500    14  5445    10  31
...

single function example with more data:

http://www.sqlfiddle.com/#!17/4e8f0/1
with t as (
  SELECT    device,
            time, 
            lead(time) over (order by time) - time as duration, 
            state
    from telemetry
    where device = 'Device_001'
    order by time asc
)  
select 
       min(t.device) as DeviceId, -- get first row value
       extract(epoch from (sum(t.duration))) as "total",
       extract(epoch from (sum(t.duration * (t.state = 0)::int))) as "state1",
       extract(epoch from (sum(t.duration * (t.state = 1)::int))) as "state2",
       extract(epoch from (sum(t.duration * (t.state = 2)::int))) as "state3",
       extract(epoch from (sum(t.duration * (t.state = 3)::int))) as "state4" 
from t;

Solution

  • with t as (
      SELECT    device,
                time, 
                lead(time) over (Partition by device order by time) - time as duration, 
                state
        from telemetry
        order by device, time asc
    )
    select 
           --min(t.device) as DeviceId, -- get first row value
           t.device,
           extract(epoch from (sum(t.duration))) as "total",
           extract(epoch from (sum(t.duration * (t.state = 0)::int))) as "state1",
           extract(epoch from (sum(t.duration * (t.state = 1)::int))) as "state2",
           extract(epoch from (sum(t.duration * (t.state = 2)::int))) as "state3",
           extract(epoch from (sum(t.duration * (t.state = 3)::int))) as "state4" 
    from t
    group by t.device
    order by t.device;
    
    

    http://www.sqlfiddle.com/#!17/46edf2/13

    with thanks to determine duration of certain state of a device in table with multiple devices