以锯齿形模式选择最大值(局部最大值)

时间:2018-09-13 11:19:45

标签: sql postgresql

我在postgres中有一个名为“ ts”的表,如下所示:

dev -- numeric device id
ts -- Unix epoch timestamp
key -- string (only interested in records where key is 'u')
val -- int representing uptime in ms

我将进程正常运行时间记录到此表中,并且每30秒左右记录一次。结果是表的val不断增加,当进程重新启动时,val会定期重置为0,从而创建类似锯齿的模式。请注意,实际记录的值可能不为0,因为重新启动后可能不会立即进行记录。

我想做的是通过定期挑选不代表复位前最大正常运行时间的值来清理表。同一张表中混合了多个设备,因此最长正常运行时间必须按dev分组。

示例:

dev ts      key  val
1   100000  'u'  50      -- boring
1   130100  'u'  30050   -- delete this
1   160100  'u'  60050   -- >> keep this one
1   190200  'u'  100     -- this record dies
1   220200  'u'  30100   -- >> keep this one too
1   250200  'u'  300    

我希望查询能挑选出除我上面标记的记录以外的所有记录,这些记录没有意思,可以删除。

淘汰将在批处理过程中定期进行。

3 个答案:

答案 0 :(得分:1)

相反,为什么不更新相关记录本身呢?并且仅在进程重新启动时才插入新记录。

答案 1 :(得分:1)

如果只想保留局部最大值,则可以使用lead()lag()

select t.*
from (select t.*,
             lead(val) over (partition by dev order by ts) as next_val,
             lag(val) over (partition by dev order by ts) as prev_val
      from t
      where key = 'u'
     ) t
where val > prev_val and val > next_val;

答案 2 :(得分:1)

因为它很有趣:在窗口函数中使用了新的 PostgreSQL 11 功能“ GROUPS”来解决局部极大值的问题。

问题:

dev key ts      val
1   u   100000  50
1   u   130100  30050
1   u   160100  60050 -- really single local maximum
1   u   190200  100
1   u   220200  30100 -- local maximum together with next value
1   u   250200  30100 
1   u   300000  300
1   u   500000  100
1   u   550000  1000  -- a (tied) local maximum if only 1 before and 1 after is used, which is wrong
1   u   600000  1000
1   u   650000  2000  -- real local maximum together with 2 next rows
1   u   700000  2000
1   u   720000  2000
1   u   750000  300

新的PostgreSQL 11功能:

JOOQ Blog Post explains the feature

The Postgres 11 documentation

demo: db<>fiddle

SELECT 
    dev, key, ts, val 
FROM (
     SELECT
        *, 
        -- B:
        max(val) over (order by sum, val GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as local_max 
     FROM (
        SELECT -- A
            *, 
            sum(is_diff) over (order by ts) 
        FROM (
            SELECT 
                *,
                CASE WHEN val = lag(val) over (order by ts) THEN 0 ELSE 1 END as is_diff
            FROM test t
            WHERE key = 'u'
        )s
    )s
)s 
WHERE val = local_max

A:这只是准备工作。窗口函数需要一定顺序。如果您要进行PARTITION BY val,则该表将首先由val进行排序。但是在此示例中,您想按ts保留订单。然后,您想要val的窗口函数魔术。因此,在这一部分中,我将通过保持ts的顺序来计算紧随其后的行中相同值的组号。 (Maybe this could be done in a better way?

结果是这样的:

dev key ts      val     is_diff  sum
1   u   100000  50      1        1
1   u   130100  30050   1        2
1   u   160100  60050   1        3
1   u   190200  100     1        4
1   u   220200  30100   1        5     \ same group
1   u   250200  30100   0        5     /
1   u   300000  300     1        6
1   u   500000  100     1        7
1   u   550000  1000    1        8     \ same group
1   u   600000  1000    0        8     /
1   u   650000  2000    1        9     \
1   u   700000  2000    0        9     | same group
1   u   720000  2000    0        9     /
1   u   750000  300     1        10

B:这是Postgres 11的新功能。现在可以检查组的值。在过去,可以查找固定的行号。但是现在您可以检查下一组的值。说:如果您有3个具有相同值的三行,则无论您绑定多少行,都可以检查未绑定的下一个或上一个值。这样可以很酷地解决问题:

对于具有两个1000值的示例:现在我们可以检查:下一个值是否大于当前值?不,是一样的。所以是同一组。因此,让我们看看下面的行。那是2000,而且更大。因此,当前行不能是局部最大值。

在此组窗口中,您可以获取周围组的最大值,即使存在捆绑值,该组也可以为您提供本地值。