如何计算给定状态的列状态的总时间

时间:2017-11-02 13:14:59

标签: mysql sql

在MySql中我有一个表 device ,其中包含timestamp(long)和status(boolean)的列:

| ts   | status |
+------+--------+
| 1    |      1 |
| 2    |      1 |
| 5    |      1 |
| 17   |      0 |
| 18   |      0 |
| 26   |      0 |
| 31   |      1 |
| 32   |      1 |
| 37   |      1 |

需要查询来计算状态为1的时间。 对于给定的情况,它是:(17-1) + (37-31) = 22

3 个答案:

答案 0 :(得分:1)

http://sqlfiddle.com/#!9/e27644/1

第一步创建一个视图ordered_status (mysql不支持窗口函数,如排名;-()

(您可以通过在position表格中直接添加AUTO_INCREMENT device列来跳过此视图

create view ordered_status as
select   
    count(*) position,
    A.ts,
    A.status    
from
    device A
    left join device B on
        A.ts >= B.ts
group by
    A.ts,A.status

第二步,将每一行加入下一行并仅计算status=1的时间戳增量,sum总计:

select
    sum(case
    when A.status=1 then B.ts-A.ts
    else 0   
    end)        
from
    ordered_status A
    left join ordered_status B on 
        A.position+1 = B.position

或无视图http://sqlfiddle.com/#!9/97b97/1

select
    sum(case
    when A.status=1 then B.ts-A.ts
    else 0   
    end)        
from
    (select count(*) position, A.ts,A.status    
       from device A left join device B on A.ts >= B.ts
      group by A.ts,A.status) A
    left join 
    (select count(*) position, A.ts,A.status    
       from device A left join device B on A.ts >= B.ts
      group by A.ts,A.status) B on 
        A.position+1 = B.position
order by
    A.position

答案 1 :(得分:0)

由于您无法使用窗口功能,解决方案稍微复杂一些

select sum(final.ts * final.sign)
from
(
    select device.ts,
           case when status = 1 and (prevstatus is null or prevstatus = 0) then -1  else 1 end sign
    from
    (
        select d1.ts, d2.status, prev.ts prevts, prev.status prevstatus, next.ts nextts, next.status nextstatus
        from (
          select d1.ts,
               d1.status,
               max(d2.ts) prevts
          from device d1
          left join device d2 on d1.ts > d2.ts
          group by d1.ts, d1.status
        ) d1 
        left join (
          select d1.ts,
               d1.status,
               min(d2.ts) nextts
          from device d1
          left join device d2 on d1.ts < d2.ts
          group by d1.ts, d1.status
        ) d2 on d1.ts = d2.ts
        left join device prev on prev.ts = d1.prevts
        left join device next on next.ts = d2.nextts
    ) devicePrevNext
    where (status = 1 and (prevstatus is null or prevstatus = 0 or nextstatus is null)) or
          (status = 0 and prevstatus = 1)
) final

demo

答案 2 :(得分:0)

我会将此短语称为“获取下一个0”并将该信息用于总和:

select next_ts_0, min(ts) as ts_1, max(ts) as ts_1_end
from (select d.*,
             (select d2.ts
              from device d2
              where d2.status = 0 and d2.ts > d.ts
              order by d2.ts asc
              limit 1
             ) as next_ts_0
      from device d
     ) d
where status = 1
group by next_ts_0;

然后你可以加上结果:

select sum(coalesce(next_ts_0, ts_1_end) - ts_1)
from (select next_ts_0, min(ts) as ts_1, max(ts) as ts_1_end
      from (select d.*,
                   (select d2.ts
                    from device d2
                    where d2.status = 0 and d2.ts > d.ts
                    order by d2.ts asc
                    limit 1
                   ) as next_ts_0
            from device d
           ) d
      where status = 1
      group by next_ts_0
     ) d;

Here是此解决方案的SQL小提琴。

device(status, ts)上的索引可能有所帮助。如果性能是一个问题,您可能需要使用变量的解决方案。