I have the following table:
x
id | customer_id | status | updated_on
--------------------------------------------------
1 | 1 | 'ok' | 2021-03-01
2 | 1 | 'update_needed' | 2021-03-03
3 | 1 | 'ok' | 2021-03-04
4 | 2 | 'ok' | 2021-03-07
5 | 1 | 'update_needed' | 2021-03-11
6 | 1 | 'request_info' | 2021-03-17
7 | 2 | 'request_info' | 2021-03-17
8 | 3 | 'ok' | 2021-03-17
9 | 4 | 'ok' | 2021-04-25
10 | 2 | 'blocked' | 2021-03-25
11 | 2 | 'request_info' | 2021-03-29
12 | 4 | 'request_info' | 2021-04-07
I am looking for a way to get all customers whose status is currently not ‘ok’ with an indication since when the status is not ‘ok’. The result should be:
customer_id | duration
-------------------------------------------
1 | 29 days
2 | 23 days
4 | 2 days
My attempt is the following, but it gives incorrect values for customers that are currently ‘ok’.
SELECT
customer_id,
MIN(duration) AS duration
FROM (
SELECT
customer_id,
status,
updated_on,
AGE(NOW(), LEAD(updated_on) OVER w) as duration
FROM
customer_status
WINDOW w AS (
PARTITION BY customer_id
ORDER BY updated_on
)
ORDER BY updated_on DESC
) x
WHERE status = 'ok'
GROUP BY customer_id
ORDER BY duration;
The thing that I try to figure out is getting the first row for each customer with a not-‘ok’ status, since the last ‘ok’-status. How can I achieve this in Postgres?
Advertisement
Answer
You can assign a grouping to the rows based on the count of 'ok'
statuses on or after each row.
Then, just filter where the count is 0
and aggregate:
select customer_id, current_date - min(updated_on) as days_not_ok
from (select cs.*,
count(*) filter (where status = 'ok') over (partition by customer_id order by updated_on desc) as grp
from customer_status cs
where updated_on <= current_date
) cs
where grp = 0
group by customer_id;
Here is a db<>fiddle.