Reputation: 309
I have a table with a series of dates, customer ID's, and integers that updates each week with a new date and integer values for all ID's.
I'd like to be able to write a query that will detect if the most recent three values are in decreasing order, and return TRUE or FALSE, for each ID.
So, for the table:
ID | Weekending | Value
1 | 2015-07-15 | 5
2 | 2015-07-15 | 23
1 | 2015-07-08 | 7
2 | 2015-07-08 | 21
1 | 2015-07-01 | 9
2 | 2015-07-01 | 24
I'd want a query that would return TRUE for ID:1 because three consecutive values declined, but FALSE for ID:2 because the value increased.
Upvotes: 4
Views: 743
Reputation: 44871
Not sure about performance but using the lag
window function like this should work:
select *,
case when
value < lag(value, 1) over (partition by id order by weekending)
and value < lag(value, 2) over (partition by id order by weekending)
then 'True' else 'False' end
from t
order by id, weekending desc;
This would mark the row 1 | 2015-07-15 | 5
as true, and all other rows as false.
To only get the last row per id you could do this:
select ID, Weekending, Value, decrease
from (
select *,
case when
value < lag(value, 1) over (partition by id order by weekending)
and value < lag(value, 2) over (partition by id order by weekending)
then 'True' else 'False' end as decrease,
row_number() over (partition by id order by weekending desc) as r
from t
) src
where r = 1;
which would give you a result like:
ID Weekending Value decrease
1 2015-07-15 5 True
2 2015-07-15 23 False
Upvotes: 4
Reputation: 75896
SELECT DISTINCT ON (id)
id, week,val,val1,val2, val< val1 AND val1 < val2 as decreases
FROM (
SELECT id,week,val,
lag(val,-1,10000) OVER w1 AS val1,
lag(val,-2,10001) OVER w1 AS val2
FROM TAB
WINDOW w1 AS (PARTITION BY id ORDER BY week DESC)
ORDER BY ID,week DESC)
AS X
This assumes that 10000 and 10001 are "very large values" (true values will always be below that), and that you are interested in strict decreasing sequences.
For example, for the data
id week val
1 2015-07-15 5
1 2015-07-08 7
1 2015-07-01 9
1 2015-06-28 121
1 2015-06-15 2
2 2015-07-15 23
2 2015-07-08 21
2 2015-07-01 24
3 2015-07-15 20
3 2015-07-08 21
4 2015-07-15 30
4 2015-07-08 21
5 2015-07-08 210
this gives
id week val val1 val2 decreases
1 2015-07-15 5 7 9 t
2 2015-07-15 23 21 24 f
3 2015-07-15 20 21 10001 t
4 2015-07-15 30 21 10001 f
5 2015-07-08 210 10000 10001 t
Upvotes: 0
Reputation: 52336
If you can select the relevant records based only on the weekending values (so assuming that all ids have records for every recent week), you could select the values into an array and use array methods to detect whether the values are descending or not:
with cte_values as (
select id,
array_agg(value order by weekending) value_array
from my_table
where weekending >= <add something to select the relevant weeks>)
select id
case
when value_array[2] < value_array[1] and
value_array[3] < value_array[2]
then true
else false
end is_ascending
from cte_values
Not syntax checked
Upvotes: 0