【问题标题】:Determining how often and how long a specific event occurs in SQL data确定特定事件在 SQL 数据中发生的频率和持续时间
【发布时间】:2019-06-13 18:21:28
【问题描述】:

我很好奇在使用 Microsoft SQL Server Management Studio 17 管理的一组 SQL 数据中找出事件发生频率和发生时间的最佳方法是什么。

下面是一个简化的数据表,用于说明我有兴趣解决的问题类型。假设传感器每 100 毫秒收集一次数据,我想知道电源降至 0 的频率和时间。

我有几个想法如何使用 CTE 和/或 Window 函数来执行此操作,但是我对这些函数的理解似乎并没有在 SQL Management Studio 中转化,因为我的代码在理论上应该正确的点上不断出现错误。

例如,我认为我可以按位置编号划分窗口函数,在功率为 0 时按点过滤,然后从 FIRST_VALUE 中减去 LAST_VALUE。然而,环境不承认这些论点。

我还想到了一个 CTE,它已经过滤掉了功率为零的点,但我无法将它带到远程功能点。

CREATE TABLE SensorData
(
    [TimeStamp] DATETIME ,
    [Position] INT,
    [POWER] INT
);

INSERT INTO SensorData ([TimeStamp], [Position], [Power])
VALUES (4, 1, 59), (101, 1, 60), (207, 1, 50), (321, 1, 58),
       (428, 1, 55), (534, 1, 59), (646, 1, 51), (755, 1, 0),
       (868, 1, 0), (975, 1, 0), (1081, 1, 0), (1193, 2, 45),
       (1307, 2, 52), (1412, 2, 51), (1519, 2, 55), (1629, 2, 58),
       (1735, 2, 0), (1851, 2, 0), (1960, 2, 0), (2066, 2, 54);

SELECT *
FROM SensorData;

最后的输出看起来并不那么重要。重要的是我知道在这种情况下功率为零的事件数量以及该事件持续了多长时间(事件中的最后一个时间戳减去第一个时间戳)

任何建议将不胜感激!

【问题讨论】:

  • 你能发布预期的输出吗?

标签: sql sql-server time-series


【解决方案1】:

在多个 CTE 中执行此操作以使事情井井有条,可以按如下方式完成:

with sensorevents as (
    select
        [TimeStamp]
      , position
      , power
      , lag(power,1) over (order by timestamp) as prevPower
    from SensorData
)
, powerloss as (
    select
        *
        , case when [prevPower] > 0 and power = 0 then 'power loss'
               when [prevPower] = 0 and power > 0 then 'power on'
          end as status
        , case when [prevPower] = 0 then lag(timestamp,1) over (order by timestamp)
          end as powerOffTimestamp
        , case when [prevPower] > 0 and power = 0 then 0
               when [prevPower] = 0 and power > 0 then timestamp - lag(timestamp,1) over (order by timestamp)
          end as duration
    from Sensorevents
    where ([prevPower] > 0 and power = 0)
           or
          ([prevPower] = 0 and power > 0)
)
select
    *
 from powerloss
 where status = 'power on'

第一个 CTE 定义了一个新列 prevPower,它告诉我们我们是否处于发生断电或恢复供电的边缘。下一个 CTE 再次使用这些边缘和窗口函数来找到上一个事件(丢失)发生的时间戳,用于恢复电源事件,并根据时间戳差计算持续时间。

最后的select 语句只是过滤电源恢复事件:

TimeStamp   position    power   prevPower   nextPower   status  powerOffTimestamp   duration
09/04/1903 00:00:00 2   45  0   52  power on    26/01/1902 00:00:00 15/03/1901 00:00:00
29/08/1905 00:00:00 2   54  0       power on    02/10/1904 00:00:00 28/11/1900 00:00:00

我刚刚看到position 的其他回复分区。要将其添加到此解决方案中,您需要通过添加 partition by position 子句来修改所有窗口函数:

      , lag(power,1) over (order by timestamp partition by position) as prevPower

https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=264deed484604cda3ace1fb60d674068

【讨论】:

  • 谢谢,我知道 'case when' 子句,但无法正确实现语法(对不起,我的 ubels 延迟响应)
【解决方案2】:

这是一个孤岛问题。您需要为“0”记录分配一个组。组的一个方便标识符是值之前的非零值的数量。剩下的就是聚合:

select position, min(timestamp), max(timestamp)
from (select sd.*,
             sum(case when power <> 0 then 1 else 0 end) over (partition by position order by timestamp) as grp
      from sensordata sd
     ) sd
where power = 0
group by position, grp;

请注意,这假定您需要每个 position0s。

特别是因为您正在查看power = 0,您可以简化组的定义:它是到该点的幂的总和。对于具有power = 0 的一组相邻行,这是恒定的:

select position, min(timestamp), max(timestamp)
from (select sd.*,
             sum(power) over (partition by position order by timestamp) as grp
      from sensordata sd
     ) sd
where power = 0
group by position, grp;

【讨论】:

  • 这就是我最初尝试的方法,感谢您清除该方法并为迟到的回复感到抱歉
猜你喜欢
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多