0

For context:

I am playing with a filling level sensor, a water tank and a watering pump and have the following problem:

  1. I have an initial filling value for my tank.

  2. Each time step, I am subtracting a certain amount (the outflow) from this init value via a window function, in order to get a time series with the filling level.

  3. Should that running sum get below a lower threshold, it gets "refilled" to an upper threshold for the refill.

  4. From there, I want to keep subtracting until it hits the lower value again, then refill, etc.

  5. I would like to sum up the refill amount over the series

Something like the following:

Example plot

Assuming I start from 10, and refill from 5 as lower limit to 8 as upper limit. The table should look something like this, with running sum being the amount of water in the tank, refill_amount as the amount that was needed to refill, and/or refill_running_sum to get a final, total amount for refilling:

 time_step | running_sum | refill_amount | refill_running_sum   |
 ----------+-------------+---------------+----------------------|      
      1          10             0                 0         
      2           9             0                 0         
      3           8             0                 0         
      4           7             0                 0         
      5           6             0                 0         
      6  -  -  -  5  -   -   -  3  -   -   -   -  3  -  --refill
      7           8             0                 3         
      8           7             0                 3         
      9           6             0                 3         
      10 -  -  -  5  -   -   -  3  -   -   -   -  6  -  --refill     
      11          8             0                 6        
      12          7             0                 6         
      13          6             0                 6         
      14 -  -  -  5  -   -   -  3  -   -   -   -  9  -  --refill     
      15          8             0                 9         

I got as far as the SQL below dbfiddle here:

Using a CASE statement, I get to refill the amount once and get the first amount of water to refill. But I would need a CTE with a case for every single refill.

Apologies as I know I'm missing SQL vocabulary here to properly approach this. Yet thank you for any pointers!

    /* Generate some mockup series with init+threshold limits */ 
    WITH vals AS ( 
        SELECT 
            generate_series(1,30,1) AS time_step
            ,10 AS init_val
            ,8 AS upper_limit_refill
            ,5 AS lower_limit_refill
            ,random() AS random_val
    ), 
    /* Create the window function subtracting a mockup val */
    example_series AS ( 
        SELECT 
            *   
            ,init_val-sum(random_val) OVER(ORDER BY time_step ASC) AS running_amount
        FROM vals
    )
    SELECT 
        time_step
        ,running_amount
        /* I manage to get the amount needed to refill ONCE, but I'd need a new CTE and CASE for each refill like this*/
        ,CASE 
            WHEN running_amount < lower_limit_refill 
            THEN upper_limit_refill +  running_amount -sum(random_val) OVER(ORDER BY time_step ASC  ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) 
            ELSE 0 
        END AS refill_once
        ,CASE 
            WHEN running_amount < lower_limit_refill 
            THEN upper_limit_refill - running_amount
            ELSE 0 
        END AS refill_to_limit_amount

    FROM example_series

1 Answer 1

0

I don't know if window function could be used here. I'd go with a simple recursive :

 WITH vals AS ( 
   SELECT generate_series(1,30,1) AS time_step
         ,10.0 AS init_val
         ,8.0 AS upper_limit_refill
         ,5.0 AS lower_limit_refill
         ,(random())::numeric AS random_val
  ),
vCalc as (
   with recursive vRec as (
    select time_step, 
           init_val, 
           0::numeric refill,
           random_val,  
           init_val - random_val as final_val 
     from vals 
     where time_step = 1
    union all
      select v.time_step, 
            r.final_val as init_val, 
            case when r.final_val < lower_limit_refill then upper_limit_refill - r.final_val else 0 end refill, 
            v.random_val,  
            r.final_val - v.random_val + case when r.final_val < lower_limit_refill then upper_limit_refill - r.final_val else 0 end as final_val
    from vRec r
    inner join vals v on v.time_step = r.time_step + 1
)
select * from vrec
)
select * from vCalc;

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.