PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Somma cumulativa con base dinamica in Postgres

Crea la tua funzione di aggregazione , che può essere utilizzata come funzione finestra.

Funzione di aggregazione specializzata

È più facile di quanto si possa pensare:

CREATE OR REPLACE FUNCTION f_sum_cap50 (numeric, numeric)
  RETURNS numeric LANGUAGE sql AS
'SELECT CASE WHEN $1 > 50 THEN 0 ELSE $1 END + $2';

CREATE AGGREGATE sum_cap50 (numeric) (
  sfunc    = f_sum_cap50
, stype    = numeric
, initcond = 0
);

Quindi:

SELECT *, sum_cap50(val) OVER (PARTITION BY fk
                               ORDER BY created) > 50 AS threshold_met 
FROM   test
WHERE  fk = 5;

Risultato esattamente come richiesto.

db<>violino qui
Vecchio sqlfiddle

Funzione di aggregazione generica

Per farlo funzionare per qualsiasi soglia e qualsiasi tipo di dati (numerico) e anche consenti NULL valori :

CREATE OR REPLACE FUNCTION f_sum_cap (anyelement, anyelement, anyelement)
  RETURNS anyelement
  LANGUAGE sql STRICT AS
$$SELECT CASE WHEN $1 > $3 THEN '0' ELSE $1 END + $2;$$;

CREATE AGGREGATE sum_cap (anyelement, anyelement) (
  sfunc    = f_sum_cap
, stype    = anyelement
, initcond = '0'
);

Quindi, per chiamare con un limite, diciamo, di 110 con qualsiasi tipo numerico:

SELECT *
     , sum_cap(val, '110') OVER (PARTITION BY fk
                                 ORDER BY created) AS capped_at_110
     , sum_cap(val, '110') OVER (PARTITION BY fk
                                 ORDER BY created) > 110 AS threshold_met 
FROM   test
WHERE  fk = 5;

db<>violino qui
Vecchio sqlfiddle

Spiegazione

Nel tuo caso non dobbiamo difenderci da NULL valori da val è definito NOT NULL . Se NULL può essere coinvolto, definire f_sum_cap() come STRICT e funziona perché (per documentazione ):

Sia la funzione che l'aggregato accettano un altro argomento. Per il polimorfico variante può essere un tipo di dati hardcoded o lo stesso tipo polimorfico degli argomenti principali.

Informazioni sulle funzioni polimorfiche:

Nota l'uso di letterali stringa non tipizzati , non valori letterali numerici, che per impostazione predefinita sarebbero integer !