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

Aggregato PostgreSQL personalizzato per la media circolare

Puoi utilizzare un ARRAY digitare internamente. Il tipo di argomento può comunque essere qualsiasi tipo numerico. Dimostrazione con float (=double precision ):

CREATE OR REPLACE FUNCTION f_circavg (float[], float)
  RETURNS float[] LANGUAGE sql STRICT AS
'SELECT ARRAY[$1[1] + sin($2), $1[2] + cos($2), 1]';

CREATE OR REPLACE FUNCTION f_circavg_final (float[])
  RETURNS float  LANGUAGE sql AS
'SELECT CASE WHEN $1[3] > 0 THEN atan2($1[1], $1[2]) END';

CREATE AGGREGATE circavg (float) (
   sfunc     = f_circavg
 , stype     = float[]
 , finalfunc = f_circavg_final
 , initcond  = '{0,0,0}'
);

La funzione di transizione f_circavg() è definito STRICT , quindi ignora le righe con NULL ingresso. Imposta anche un terzo elemento dell'array per identificare gli insiemi con una o più righe di input, altrimenti il ​​CASE la funzione finale restituisce NULL .

Tabella temporanea per il test:

CREATE TEMP TABLE t (x float);
INSERT INTO t VALUES (2), (NULL), (3), (4), (5);

Ho inserito un NULL valore per testare anche STRICT Magia. Chiama:

SELECT circavg(x) FROM t;

       circavg
-------------------
 -2.78318530717959

Controllo incrociato:

SELECT atan2(sum(sin(x)), sum(cos(x))) FROM t;

       atan2
-------------------
 -2.78318530717959

Restituisce lo stesso. Sembra funzionare. Nel test con una tabella più grande, l'ultima espressione con funzioni di aggregazione regolari era 4 volte più veloce dell'aggregazione personalizzata.

Verifica per zero righe di input / solo input NULL:

SELECT circavg(x) FROM t WHERE false;     -- no input rows
SELECT circavg(x) FROM t WHERE x IS NULL; -- only NULL input

Restituisce NULL in entrambi i casi.