2017-03-31 154 views
1

我在表SQL计算的总时间在某一特定国家

_____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  

有这些DATAS我想计算总时长各变量在状态1两个日期

之间对于例如,对于VAR1 2017年3月31日00:00和2017年4月1日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之间1小时以及2017年4月1日00:00:00(无前值,但因为其更改为1,我想,这是0)之前

所以结果我要为VAR2应该20小时

可变 | __时间值(秒)

变量1 | 180

Variable2 | 1200

如果有人能帮助我。

在此先感谢

+0

按小时这是永远? – SqlZim

回答

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快得多。

数量和日历表参考:

+0

非常感谢您的回答,它工作得非常好。事实上,我只想在每个变量的特定状态下持续时间(并且在小时内不是个好主意)! –

+0

更正@SqlZim。它不起作用,因为它给了我2017-03-29 23:00:00和2017-03-31 01:00:00之间的时间。而且因为我的查询是在2017-03-31 00:00:00开始我只希望2017-03-31 00:00:00和2017-03-31 01:00:00之间的小时00:00:00 –

+0

@StéphanF更新后才能使用'分钟(h.datehour)' – SqlZim