程序功能
基本问题
声明数组变量的维度,就像float8[][]
二维数组一样,仅用于文档。考虑这个相关答案中的细节:
mapping postgresql text[][] type and Java type
您正在混淆一维和二维数组。在声明二维数组(无效)时,您只能将其设为一维数组。
要初始化数组,请使用array_fill()
:
img := array_fill(0, ARRAY[rows,cols])
此示例生成一个二维数组 - 与您的错误语句相反,生成一个一维数组:
img := ARRAY( SELECT 0 FROM generate_series(1, rows* cols) );
显示的数组下标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
参数。
优越的基于集合的版本
plpgsql 中的循环和单独分配相对较慢。正如@Craig 在此相关答案中所解释的那样,数组处理的性能特别差:
为什么 PostgreSQL 数组访问在 C 中比在 PL/pgSQL 中快得多?
我会改用基于集合的操作,使用更大的数字会更快。
多维数组的聚合函数
要生成多维数组,我们需要一个自定义聚合函数。array_agg()
或者数组构造函数只生成一维数组。这很简单,正如我们在这个相关答案中得出的那样:
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!