【问题标题】:SQL Server query to get continues occurrencesSQL Server 查询以获取持续事件
【发布时间】:2016-11-30 11:41:48
【问题描述】:

最近我遇到了一个要求,如果我有我的源表,

Machine_Name|    Time             |   Alarm 
------------|---------------------|---------
Mac1        |    2016-11-22 05:15 |   0     
Mac1        |    2016-11-22 05:30 |   1     
Mac1        |    2016-11-22 05:45 |   1     
Mac1        |    2016-11-22 06:00 |   0     
Mac1        |    2016-11-22 06:15 |   1     
Mac1        |    2016-11-22 06:30 |   1     
Mac1        |    2016-11-22 06:45 |   1     
Mac1        |    2016-11-22 07:00 |   1     
Mac1        |    2016-11-22 07:15 |   1     
Mac1        |    2016-11-22 07:30 |   1     
Mac2        |    2016-11-22 05:15 |   0     
Mac2        |    2016-11-22 05:30 |   0     
Mac2        |    2016-11-22 05:45 |   1     
Mac2        |    2016-11-22 06:00 |   1     
Mac2        |    2016-11-22 06:15 |   1     
Mac2        |    2016-11-22 06:30 |   1     
Mac2        |    2016-11-22 06:45 |   0     
Mac2        |    2016-11-22 07:00 |   1     

上表存储设施中安装的不同机器的详细信息及其每个时段(每 15 分钟间隔)的“发出警报”状态。

现在我需要计算此警报的相关性。对于任何单个警报,相关性为 25%。如果一台机器连续 4 个时间段(1 小时)发出警报,则它将最大相关性存档 100%。

如果警报持续超过 4 个连续时间段(1 小时),则相关性保持 100%。

从上面的源表得到的预期结果集如下,

Machine_Name|    Time             |   Alarm |  Alert_Relevancy(%)
------------|---------------------|---------|---------------
Mac1        |    2016-11-22 05:15 |   0     |  0 
Mac1        |    2016-11-22 05:30 |   1     |  25 
Mac1        |    2016-11-22 05:45 |   1     |  50 
Mac1        |    2016-11-22 06:00 |   0     |  0 
Mac1        |    2016-11-22 06:15 |   1     |  25 
Mac1        |    2016-11-22 06:30 |   1     |  50 
Mac1        |    2016-11-22 06:45 |   1     |  75 
Mac1        |    2016-11-22 07:00 |   1     |  100 
Mac1        |    2016-11-22 07:15 |   1     |  100
Mac1        |    2016-11-22 07:30 |   1     |  100
Mac2        |    2016-11-22 05:15 |   0     |  0 
Mac2        |    2016-11-22 05:30 |   0     |  0 
Mac2        |    2016-11-22 05:45 |   1     |  25 
Mac2        |    2016-11-22 06:00 |   1     |  50 
Mac2        |    2016-11-22 06:15 |   1     |  75 
Mac2        |    2016-11-22 06:30 |   1     |  100 
Mac2        |    2016-11-22 06:45 |   0     |  0 
Mac2        |    2016-11-22 07:00 |   1     |  25 

如果我还可以获得一个查询以仅选择那些连续引发至少 4 次(相关性达到 100)的警报系列,那就太好了。

如果我已经删除了连续至少 4 次不为 1 的任何警报系列,则预期的第二个结果集如下所示。

Machine_Name|    Time             |   Alarm |  Alert_Relevancy(%)
------------|---------------------|---------|---------------
Mac1        |    2016-11-22 06:15 |   1     |  25 
Mac1        |    2016-11-22 06:30 |   1     |  50 
Mac1        |    2016-11-22 06:45 |   1     |  75 
Mac1        |    2016-11-22 07:00 |   1     |  100 
Mac1        |    2016-11-22 07:15 |   1     |  100
Mac1        |    2016-11-22 07:30 |   1     |  100
Mac2        |    2016-11-22 05:45 |   1     |  25 
Mac2        |    2016-11-22 06:00 |   1     |  50 
Mac2        |    2016-11-22 06:15 |   1     |  75 
Mac2        |    2016-11-22 06:30 |   1     |  100 

【问题讨论】:

  • 什么 DBMS?如果 MSSQL 2012+ 您可以使用滞后和领先功能,否则您可以使用 CTE。编辑:假设您来自上一个问题..
  • 您刚刚选择了一个解决方案,该解决方案通过记录而不是基于集合的解决方案。
  • 嗨,Mortb,对不起,我没有在我首先尝试的内容中包含内容。我的错。我尝试使用 CTE 方法,但由于我的锚记录设置错误,无法达到确切的结果。但后来 LukStorms 发布了我正在寻找的确切解决方案。我将确保在问题中包含问题 sn-p。谢谢。

标签: sql sql-server


【解决方案1】:
select      Machine_Name
           ,time
           ,Alarm
           ,case when alarm_seq >= 4 then 4 else alarm_seq end * 25

from       (select      *
                       ,sum (Alarm) over 
                        (
                            partition by    Machine_Name,group_id 
                            order by        time
                        )                           as alarm_seq

            from       (select      *
                                   ,count (nullif(alarm,1)) over 
                                    (
                                        partition by    Machine_Name 
                                        order by        time
                                    )   as group_id

                        from        t
                        ) t
            ) t

select      Machine_Name
           ,time
           ,Alarm
           ,case when alarm_seq >= 4 then 4 else alarm_seq end * 25

from       (select      *
                       ,sum (Alarm) over 
                        (
                            partition by    Machine_Name,group_id 
                            order by        time
                        )                           as alarm_seq

                       ,sum (Alarm) over 
                        (
                            partition by    Machine_Name,group_id 
                        )                           as alarms

            from       (select      *
                                   ,count (nullif(alarm,1)) over 
                                    (
                                        partition by    Machine_Name 
                                        order by        time
                                    )   as group_id

                        from        t
                        ) t
            ) t

where       alarms >= 4
        and alarm = 1

【讨论】:

  • 您好 Markovitz,将您的解决方案作为 CTE 解决方案在应用于大型源时会花费更多时间来产生结果。谢谢。
  • @RonyVarghes - 听起来不错……你能分享一下这两种解决方案的执行时间吗?
【解决方案2】:

使用带有 row_number 的 CTE 和递归 CTE

SQL 使用表变量进行演示。

declare @SourceTable table (Machine_Name varchar(4), [Time] datetime, Alarm bit);

insert into @SourceTable values
('Mac1','2016-11-22 05:15',0),
('Mac1','2016-11-22 05:30',1),
('Mac1','2016-11-22 05:45',1),
('Mac1','2016-11-22 06:00',0),
('Mac1','2016-11-22 06:15',1),
('Mac1','2016-11-22 06:30',1),
('Mac1','2016-11-22 06:45',1),
('Mac1','2016-11-22 07:00',1),
('Mac1','2016-11-22 07:15',1),
('Mac1','2016-11-22 07:30',1),
('Mac2','2016-11-22 05:15',0),
('Mac2','2016-11-22 05:30',0),
('Mac2','2016-11-22 05:45',1),
('Mac2','2016-11-22 06:00',1),
('Mac2','2016-11-22 06:15',1),
('Mac2','2016-11-22 06:30',1),
('Mac2','2016-11-22 06:45',0),
('Mac2','2016-11-22 07:00',1);

;with CTE as
(
   select
   row_number() over (partition by Machine_Name order by [Time]) as rn,
   Machine_Name, [Time], Alarm
   from @SourceTable
),
RECURSIVE_CTE as 
(
   select Machine_Name, [Time], Alarm, rn, rn as rn_root, 0 as Relevancy
   from CTE
   where Alarm = 0
   UNION ALL
   select CTE.Machine_Name, CTE.[Time], CTE.Alarm, CTE.rn, R.rn_root, case when R.Relevancy = 100 then 100 else (R.Relevancy + 25) end
   from RECURSIVE_CTE R 
   JOIN CTE ON (R.Machine_Name = CTE.Machine_Name AND R.rn + 1 = CTE.rn AND CTE.Alarm = 1)
)
select R.Machine_Name, R.[Time], R.Alarm, R.Relevancy as [Alert_Relevancy(%)]
from RECURSIVE_CTE R
INNER JOIN (select Machine_Name, rn_root from RECURSIVE_CTE where Relevancy = 100 group by Machine_Name, rn_root) M
ON (R.Machine_Name = M.Machine_Name and R.rn_root = M.rn_root)
where R.Relevancy > 0
order by R.Machine_Name, R.[Time];

返回:

Mac1    2016-11-22 06:15:00.000 1   25
Mac1    2016-11-22 06:30:00.000 1   50
Mac1    2016-11-22 06:45:00.000 1   75
Mac1    2016-11-22 07:00:00.000 1   100
Mac1    2016-11-22 07:15:00.000 1   100
Mac1    2016-11-22 07:30:00.000 1   100
Mac2    2016-11-22 05:45:00.000 1   25
Mac2    2016-11-22 06:00:00.000 1   50
Mac2    2016-11-22 06:15:00.000 1   75
Mac2    2016-11-22 06:30:00.000 1   100

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 2013-01-31
    相关资源
    最近更新 更多