将 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
=> least
和 max
=> 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';
我被问到如何对一行中的可变值列表进行标准偏差。例如:
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
=> least
和 max
=> 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';