SQL计算特定状态的总时间

时间:2017-03-31 12:17:28

标签: sql-server date

我在表中有这些数据

_____DateTime____|Variable__|Value
2017/03/29 23:00:00 | Variable1   | 1     
2017/03/31 01:00:00 | Variable1   | 0     
2017/03/31 02:00:00 | Variable1   | 1     
2017/03/31 03:00:00 | Variable1   | 0     
2017/03/31 04:00:00 | Variable2   | 1     
2017/03/31 23:00:00 | Variable1   | 1     
2017/04/01 01:00:00 | Variable1   | 0     

我想计算两个日期之间每个变量处于状态1的总持续时间

例如,在Var1 2017/03/31 00:00:00和2017/04/01 00:00:00之间 结果是:

1 hour between 2017/03/31 00:00:00 and 2017/03/31 01:00:00
1 hour between 2017/03/31 02:00:00 and 2017/03/31 03:00:00
1 hour between 2017/03/31 23:00:00 and 2017/04/01 00:00:00

所以我想要Var1的结果应该是3小时

例如,在Var2 2017/03/31 00:00:00和2017/04/01 00:00:00之间 结果是: 2017/03/31 04:00:00至2017/04/01 00:00:00之间1小时(之前没有价值,但因为它变为1我以前认为是0)

所以我想要Var2的结果应该是20小时

变量 | __Time in Value(second)

Variable1 | 180

变量2 | 1200

如果有人可以帮助我。

提前致谢

1 个答案:

答案 0 :(得分:1)

对于SQL Server 2012+(由于lead()concat()

使用堆叠cte生成一个小时表到inner join子查询,该子查询使用lead()窗口函数来获取由Variable分区的状态更改的下一个日期。

要适应以前的版本,请使用outer apply()为每个变量而不是dt获取下一个lead();和常规字符串连接以及正确的转换,而不是concat()

declare @fromdate datetime = '20170331 00:00:00';
declare @thrudate datetime = '20170401 00:00:00';
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, hours as (
  select top ((datediff(hour, @fromdate, @thrudate)+1))
      [DateHour]=dateadd(hour,(row_number() over (order by (select 1)) -1),@fromdate)
  from n as deka cross join n as hecto cross join n as kilo 
                cross join n as tenK cross join n as hundredK
   order by 1
)
select variable, value
  , hours = count(h.datehour)
  , start_dt = convert(varchar(20),min(h.datehour),120)
  , end_dt   = convert(varchar(20),end_dt,120)
  , txt = concat(
            count(h.datehour),' '
          , case when count(h.datehour) < 2 then 'hour' else 'hours' end
          , ' between '
          , convert(varchar(20),min(h.datehour),120)
          , ' and '
          , convert(varchar(20),end_dt,120)
    )
from hours h
  inner join (
  select 
      variable
    , value
    , start_dt = dt
    , end_dt   = case when coalesce(lead(dt) over (partition by variable order by dt),@thrudate) > @thrudate 
                        then @thrudate 
                      else coalesce(lead(dt) over (partition by variable order by dt),@thrudate) 
                      end
  from t
    ) s
    on h.datehour >= s.start_dt
   and h.datehour <  s.end_dt
  where h.datehour >= @fromdate
    and h.datehour <  @thrudate
    and s.value = 1
group by variable, value, start_dt, end_dt

rextester演示:http://rextester.com/ZBWP22523

返回:

+-----------+-------+-------+---------------------+---------------------+--------------------------------------------------------------+
| variable  | value | hours |      start_dt       |       end_dt        |                             txt                              |
+-----------+-------+-------+---------------------+---------------------+--------------------------------------------------------------+
| Variable1 |     1 |     1 | 2017-03-31 00:00:00 | 2017-03-31 01:00:00 | 1 hour between 2017-03-31 00:00:00 and 2017-03-31 01:00:00   |
| Variable1 |     1 |     1 | 2017-03-31 02:00:00 | 2017-03-31 03:00:00 | 1 hour between 2017-03-31 02:00:00 and 2017-03-31 03:00:00   |
| Variable1 |     1 |     1 | 2017-03-31 23:00:00 | 2017-04-01 01:00:00 | 1 hour between 2017-03-31 23:00:00 and 2017-04-01 01:00:00   |
| Variable2 |     1 |    20 | 2017-03-31 04:00:00 | 2017-04-01 00:00:00 | 20 hours between 2017-03-31 04:00:00 and 2017-04-01 00:00:00 |
+-----------+-------+-------+---------------------+---------------------+--------------------------------------------------------------+

如果您经常需要这样做,可以考虑创建一个实际的表格数小时。否则,使用堆叠的cte与大多数其他选项一样快,并且随着生成的值的数量增加,它比递归cte快得多。

数字和日历表参考: