将 Postgres 聚合函数转换为内联数组

Convert Postgres Aggregate Function to Inline Array

我被问到如何对一行中的可变值列表进行标准偏差。例如:

select
  name, x, y, z, stddev (x, y, z)
from foo;

select
  order_no, a, b, c, d, e, f, stddev (a, b, c, d, e, f)
from foo;

所以本质上就像 min => leastmax => greatest,我想要一种类似的方法来将聚合 stddev 转换为 "normal"函数。

我已经能够创建一个自定义函数来根据标准公式计算标准偏差,但如果可能的话,我情不自禁地更喜欢使用内置函数。我试过这个:

CREATE OR REPLACE FUNCTION std_deviation(variadic inputs numeric[])
  RETURNS numeric AS
$BODY$
DECLARE
  result numeric;
BEGIN

  select stddev (unnest (inputs))
  into result;

  return result;
end
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

它抱怨:

ERROR: set-valued function called in context that cannot accept a set

此错误消息的流量并不缺乏,但我不太清楚如何将修复应用到我的简单函数。

或者,有没有更好的方法从一开始就做到这一点?

集合返回函数 (SRF)——例如 unnest——在 SELECT 子句中是 SQL 标准的 PostgreSQL 特定扩展。而且通常不值得使用它(因为它不是它看起来的样子)。此外,SRF 不能在聚合函数中使用

改为在 FROM 子句中使用这些 SRF 函数(并在需要时使用子选择):

SELECT name, x, y, z, (SELECT stddev(v) FROM unnest(ARRAY[x, y, z]) v)
FROM foo

如果你真的想为此编写一个函数,请使用 SQL 语言(更清晰 & PostgreSQL 可以优化它们的使用):

CREATE OR REPLACE FUNCTION std_deviation(variadic inputs numeric[])
  RETURNS numeric AS
$BODY$
  SELECT stddev(v) FROM unnest(inputs) v
$BODY$
LANGUAGE SQL IMMUTABLE;

这似乎可以解决问题。


CREATE OR REPLACE FUNCTION public.std_deviation(VARIADIC inputs numeric[])
RETURNS numeric AS
$BODY$
DECLARE
result numeric;
BEGIN

with foo as (
select unnest (inputs) as bar
)
select stddev (bar)
into result
from foo;

return result;
end
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

原来 pgnumerics 已经有这个功能了。

-- test=# select pgnumerics.stdev('{1345,1301,1368,1322,1310,1370,1318,1350,1303,1299}');
--       stdev
-- ------------------
--  27.4639157198435
-- (1 row)

CREATE OR REPLACE FUNCTION pgnumerics.stdev (
  X double precision []
) RETURNS double precision
AS $$
DECLARE
  s double precision;
  N integer;
  i integer;
  xx double precision;
  sx double precision;
BEGIN
  N := array_upper(X,1) - array_lower(X,1) + 1;
  xx:= 0.0;
  sx:= 0.0;
  for i in 1..N loop
    xx:= xx + X[i]*X[i];
    sx:= sx + X[i];
  end loop;
  s := sqrt((N*xx - sx*sx) / (N*(N-1.0)));
  return s;
END;
$$ LANGUAGE 'plpgsql';

http://pgnumerics.projects.pgfoundry.org/