PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Benutzerdefiniertes PostgreSQL-Aggregat für zirkulären Durchschnitt

Sie können ein ARRAY verwenden intern eingeben. Der Argumenttyp kann weiterhin ein beliebiger numerischer Typ sein. Demonstration mit 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}'
);

Die Übergangsfunktion f_circavg() ist STRICT definiert , also ignoriert es Zeilen mit NULL Eingang. Es setzt auch ein drittes Array-Element, um Sets mit einer oder mehreren Eingabezeilen zu identifizieren - ansonsten den CASE die letzte Funktion gibt NULL zurück .

Temporäre Tabelle zum Testen:

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

Ich habe einen NULL eingefügt -Wert, um auch STRICT zu testen Magie. Aufruf:

SELECT circavg(x) FROM t;

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

Gegenprobe:

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

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

Gibt das gleiche zurück. Scheint zu funktionieren. Im Test mit einer größeren Tabelle war der letzte Ausdruck mit regulären Aggregatfunktionen 4x schneller als das benutzerdefinierte Aggregat.

Test auf null Eingabezeilen / nur NULL-Eingabe:

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

Gibt NULL zurück in beiden Fällen.