0

PostgreSQL でストアド プロシージャをプログラミングしています。アルゴリズムは、double precision数値の 2 次元配列を処理する必要があります。

私が調査した限り、Postgres での配列操作は汎用的で非常に重いものです。私が証明しようとしている単純な例では、過度の計算コストがかかります。

例:

CREATE OR REPLACE FUNCTION fill_2d_array( rows integer, cols integer) 
  RETURNS integer AS
$BODY$

DECLARE

img double precision[][];

i integer; j integer;
cont integer;

BEGIN

img  := ARRAY( SELECT 0 FROM generate_series(1, filas * columnas) ) ; 
cont:= 0;
For i IN 1..rows LOOP
    For j IN 1..cols LOOP
        img[i * cols + j] := (i * cols + j)::double precision;
        cont := cont + 1;
    END LOOP;
END LOOP;

return cont;
END;
$BODY$
  LANGUAGE plpgsql;

誰かが、2 次元配列を処理するための代替パスまたは改善を見つけるのを手伝ってくれますか?

4

1 に答える 1

2

手続き型関数

基本的な問題

  • 2 次元配列の場合のように、配列変数の次元を宣言することは、float8[][]ドキュメントを提供するだけです。この関連する回答の詳細を検討してください:
    mapping postgresql text[][] type and Java type

  • 1 次元配列と 2 次元配列を混同しています。2 次元配列を宣言しても (効果はありません)、1 次元配列になるだけです。

  • 配列を初期化するには、次を使用しますarray_fill()

    img := array_fill(0, ARRAY[rows,cols])
    

    この例では、2 次元配列が生成されます。1 次元配列を生成する誤ったステートメントとは対照的です。

    img  := ARRAY( SELECT 0 FROM generate_series(1, rows* cols) );
  • 表示される配列添字img[i * cols + j]はほとんど意味がありません。最大値は初期化した値の 2 倍になり、「範囲外」エラーが発生します。私はあなたが意味すると思います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 が説明しているように、配列処理のパフォーマンスが特に悪い:
Why is PostgreSQL array access so much fast in C than in PL/pgSQL?

代わりに、セットベースの操作を使用します。数値が大きいほど高速です。

多次元配列の集計関数

多次元配列を生成するには、カスタム集計関数が必要です。array_agg()または、配列コンストラクターが 1 次元配列のみを生成します。この関連する回答で解決したように、それは十分に単純です:
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!
于 2013-10-01T23:48:25.713 に答える