程序函数
基本问题
声明数组变量的维度,如二维数组的float8[][],仅用于文档。考虑此相关答案中的详细信息:
mapping postgresql text[][] type and Java type
您混淆了一维数组和二维数组。在声明一个二维数组(无效)时,您只能将其设为一维数组。
-
要初始化一个数组,请使用array_fill():
img := array_fill(0, ARRAY[rows,cols])
此示例生成一个二维数组 - 与您的错误语句相反,生成一个一维数组:
<strike>img := ARRAY( SELECT 0 FROM generate_series(1, rows* cols) );</strike>
显示的数组下标img[i * cols + j] 几乎没有意义。最大值将是您初始化的两倍,从而导致“越界”错误。我想你的意思是img[i][j]
工作版
所有东西放在一起它可以像这样工作:
CREATE OR REPLACE FUNCTION f_array_fill(rows integer, cols integer
, OUT img float8[][]) AS
$func$
DECLARE
i int;
j int;
BEGIN
img := array_fill(0, ARRAY[rows,cols]);
FOR i IN 1 .. rows LOOP
FOR j IN 1 .. cols LOOP
img[i][j] := (i * cols + j)::float8;
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql;
呼叫:
SELECT f_array_fill(2,3);
结果:
{{4,5,6},{7,8,9}}
为了使函数有用,返回生成的数组。为此使用OUT parameter。
卓越的基于集合的版本
Looping and individual assignments are comparatively slow in plpgsql。正如@Craig 在此相关答案中所解释的那样,数组处理的性能特别差:
Why is PostgreSQL array access so much faster in C than in PL/pgSQL?
我会改用基于集合的操作,数字越大速度越快。
多维数组的聚合函数
要生成多维数组,我们需要一个自定义聚合函数。 array_agg() 或 array constructor 仅生成一维数组。这很简单,正如我们在这个相关答案中得出的那样:
Initial array in function to aggregate multi-dimensional array
CREATE AGGREGATE array_agg_mult (anyarray) (
SFUNC = array_cat
,STYPE = anyarray
,INITCOND = '{}'
);
替代功能
利用这种美感,我们可以构建一个与上述相同的简单 SQL 函数:
CREATE OR REPLACE FUNCTION f_array_fill_sql(_rows integer, _cols integer)
RETURNS float8[][] AS
$func$
SELECT array_agg_mult(ARRAY[arr1]) AS arr2
FROM (
SELECT array_agg((i * $2 + j)::float8) AS arr1
FROM generate_series(1, $1) i
CROSS JOIN generate_series(1, $2) j
GROUP BY i
ORDER BY i
) sub
$func$ LANGUAGE sql
呼叫:
SELECT f_array_fill_sql(3,4);
结果:
{{4,5,6},{7,8,9}}
比较
对于小数字,性能差异可以忽略不计。但是第一个变体(即使现在已经优化)随着数量的增加而迅速恶化。试试:
EXPLAIN ANALYZE SELECT f_array_fill(100,100)
EXPLAIN ANALYZE SELECT f_array_fill_sql(100,100) -- ~ 50x faster!