Because it's fun: Used the new PostgreSQL 11 feature "GROUPS
in window functions" to solve the problem with tied local maxima.
The problem:
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
The new PostgreSQL 11 feature:
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: This is just a preparation part. Window functions need a certain order. If you would do a PARTITION BY val
the table would be ordered by val
first. But at this example you would like to hold the order by ts
. And then you want to the window function magic for val
. So in this part I am calculating a group number for same values in direct following rows by holding the order by ts
. (Maybe this could be done in a better way?)
The result is this:
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: This is the new Postgres 11 function. It is possible now to check values for groups. In the past it was possible to look for a fixed row number. But now you can check for the value of the next group. Speaking: If you have 3 three rows with the same value, you could check the next or previous value that is not tied no matter how many rows you tie. This solved the problem in a very cool way:
For the example with the two 1000
values: Now we can check: Is the next value greater than the current one? No, it's the same. So it's the same group. So let's have a look at the following row. That's 2000
and it's greater. So the current row cannot be a local maximum.
With this group window you can get the maximum value of the surrounded groups which gives you the local value even if there are tied values.