忽略某些连续记录时计算值

时间:2018-01-18 14:34:10

标签: sql sql-server database

我有一个服务的日志表,可以定期测量我的软件的各种指标。 (简化)表格如下:

+-------------------+--------+-----+
|      ENTRYDATETIME|  METRIC|VALUE|
|2018-01-16 12:30:00|MyMetric|    0|
|2018-01-16 13:00:00|MyMetric|    5|
|2018-01-16 13:30:00|MyMetric|   50|
|2018-01-16 14:00:00|MyMetric|   65|
|2018-01-16 14:30:00|MyMetric|   10|
|2018-01-16 15:00:00|MyMetric|    0|
|2018-01-16 15:30:00|MyMetric|   13|
|2018-01-16 16:00:00|MyMetric|   50|
|2018-01-16 16:30:00|MyMetric|   20|
|2018-01-16 17:00:00|MyMetric|    0|
+-------------------+--------+-----+

我需要一个查询来计算值字段中的数字超过40的次数,但是将连续次数计为单个事件。上表的结果应为2。

添加更多上下文:这是衡量服务等待处理的文件数量,我想测量一天中积压的出现次数。在上面的例子中,在14:00测量的积压是在13:30首次观察到的积压,所以我希望从任何结果中排除该记录。

我可以用光标一个接一个地循环遍历记录,但是我会尝试这种方法有很多不同的指标和标准,所以如果有更好的方法可以做到这一点有人指出我正确的方向吗?

4 个答案:

答案 0 :(得分:3)

此查询应返回您的预期结果

select
    count(distinct grp)
from (
    select
        *, rn1 - row_number() over (order by ENTRYDATETIME) grp
    from (
        select
            *, row_number() over (order by ENTRYDATETIME) rn1
        from
            MyTable
    ) t
    where
        VALUE > 40
) t

答案 1 :(得分:2)

这是一个差距和岛屿问题

select count(*)
from 
(
    select exceed, grn
    from
    (
        select *,
               row_number() over (order by ENTRYDATETIME) -
               row_number() over (partition by exceed order by ENTRYDATETIME) grn
        from
        (
          select *, 
               case when value > 40 then 1 else 0 end exceed
          from your_table
        ) t1
    ) t2
    where exceed = 1
    group by exceed, grn
) t3

dbfiddle demo

神奇的是识别连续的序列。这是使用两个row_number()函数实现的:第一个是全局的,第二个是每个组(在我的情况下超过)。差异允许您识别连续序列。

答案 2 :(得分:0)

假设结果返回2就足够了,这就是我会这样做的方式,但我不相信这是你需要的,因为它是如此基本以及你提到游标的事实。 GroupCTE只将两个值组合在一起,并返回两行,您可以使用count rows函数

来计算
WITH GroupCTE AS
(
    SELECT Metric,Value
    FROM Tablename
    WHERE VALUE > 40
        AND METRIC = 'MyMetric'
    GROUP BY METRIC,VALUE
)
SELECT COUNT(*) 
FROM GroupCTE

答案 3 :(得分:0)

使用lag函数的另一个选项;检查该值是否超过40且其先前值<= 40,在这种情况下将其计为1:

;with lag_t as(
  select 
    value,
    lag(value,1,0) over (order by ENTRYDATETIME) as l_value
  from t
) 
select
  sum(case when value > 40 and l_value <= 40 then 1 else 0 end) as cnt
from lag_t;

fiddle