This problem falls into the gaps-and-islands kind of problem, as long as you need to compute consecutive values of non-null amounts.
You can reliably solve this problem in 3 steps:
- flagging when there's a change of partition, by using 1 when current amount > 0 and previous amount = 0
- compute a running sum (with
SUM
) on flags generated at step 1, to create your partitioning, which to observe the number of consecutive values on
- compute a ranking (with
ROW_NUMBER
) to rank your non-null consecutive amounts in each partition generated at step 2
WITH cte AS (
SELECT *,
CASE WHEN amount > 0
AND LAG(amount) OVER(PARTITION BY key_ ORDER BY date_) = 0
THEN 1
END AS change_part
FROM tab
), cte2 AS (
SELECT *,
SUM(change_part) OVER(PARTITION BY key_ ORDER BY date_) AS parts
FROM cte
)
SELECT key_, date_, amount,
CASE WHEN amount > 0
THEN ROW_NUMBER() OVER(PARTITION BY key_, parts ORDER BY date_)
ELSE 0
END AS days
FROM cte2
ORDER BY date_ DESC
Check the demo here.
Note: This is not the most performant solution, although I'm leaving it for reference to the next part (missing consecutive dates). @Ahmed's answer is more likely to work better in this case.
If your data should ever have holes in dates (some missing records, making the consecutiveness of amounts no-more valid), you should add a further condition in Step 1, where you create the flag for changing partition.
The partition should change:
- either if when current amount > 0 and previous amount = 0
- or if current date is greater than previous date + 1 day (consecutive dates are not consecutive in time)
WITH cte AS (
SELECT *,
CASE WHEN (amount > 0
AND LAG(amount) OVER(PARTITION BY key_ ORDER BY date_) = 0)
OR date_ > LAG(date_) OVER(PARTITION BY key_ ORDER BY date_)
+ INTERVAL '1 day'
THEN 1
END AS change_part
FROM tab
), cte2 AS (
...
Check the demo here.