PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Agregado PostgreSQL personalizado para média circular


Você pode usar um ARRAY digite internamente. O tipo de argumento ainda pode ser qualquer tipo numérico. Demonstrando com 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}'
);

A função de transição f_circavg() está definido STRICT , então ele ignora linhas com NULL entrada. Ele também define um terceiro elemento de matriz para identificar conjuntos com uma ou mais linhas de entrada - caso contrário, o CASE a função final retorna NULL .

Tabela temporária para teste:
CREATE TEMP TABLE t (x float);
INSERT INTO t VALUES (2), (NULL), (3), (4), (5);

Eu joguei um NULL value para testar também o STRICT Magia. Ligar:
SELECT circavg(x) FROM t;

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

Verificação cruzada:
SELECT atan2(sum(sin(x)), sum(cos(x))) FROM t;

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

Retorna o mesmo. Parece funcionar. No teste com uma tabela maior, a última expressão com funções de agregação regulares foi 4x mais rápida que a agregação personalizada.

Teste para linhas de entrada zero/somente entrada NULL:
SELECT circavg(x) FROM t WHERE false;     -- no input rows
SELECT circavg(x) FROM t WHERE x IS NULL; -- only NULL input

Retorna NULL em ambos os casos.