PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Agrégat PostgreSQL personnalisé pour la moyenne circulaire

Vous pouvez utiliser un ARRAY taper en interne. Le type d'argument peut toujours être n'importe quel type numérique. Démonstration avec 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 fonction de transition f_circavg() est défini STRICT , donc il ignore les lignes avec NULL saisir. Il définit également un troisième élément de tableau pour identifier les ensembles avec une ou plusieurs lignes d'entrée - sinon le CASE la fonction finale renvoie NULL .

Table temporaire pour test :

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

J'ai ajouté un NULL valeur pour tester également le STRICT la magie. Appel :

SELECT circavg(x) FROM t;

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

Contre-vérification :

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

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

Renvoie le même. Semble fonctionner. Lors d'un test avec une table plus grande, la dernière expression avec des fonctions d'agrégation régulières était 4 fois plus rapide que l'agrégat personnalisé.

Testez les lignes d'entrée nulles / uniquement l'entrée NULL :

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

Renvoie NULL dans les deux cas.