2011-12-21 13 views
28

Pgクエリは配列を返します。私は小数点以下3桁にフォーマットされた各要素でそれを取得したいと思います。どのようにして配列の各要素に関数を適用できますか? (当然、間違った)次のようなもの -Postgresの配列列の各要素に関数を適用するには?

SELECT Round(ARRAY[1.53224,0.23411234], 2); 
{1.532, 0.234} 

私はPerlのmap機能のようなものを探していますね。

+0

素晴らしい提案です。私は、この種の 'map'関数を常に適用する必要があるので、格納されたprocに行くと思います。格納されたprocに関数を渡すことができれば、それを真の 'map'関数に変換するfactory格納されたprocにすることができればさらに大きくなるでしょう。しかし、これは今のところうまくいくでしょう。もう一度、ありがとう。 – punkish

+0

Re:関数を渡す:http://stackoverflow.com/questions/8346065/function-as-parameter-to-another-function-in-postgresに興味があります。 (これは理想とはほど遠いですが、そこからいくつか使用するかもしれません) – ruakh

答えて

8

ストアドファンクションを作成する必要があります。ここでは何が必要ないものは次のとおりです。

CREATE OR REPLACE FUNCTION array_round(float[], int) 
RETURNS float[] 
AS 
$$ 
DECLARE 
    arrFloats ALIAS FOR $1; 
    roundParam ALIAS FOR $2; 
    retVal float[]; 
BEGIN 
    FOR I IN array_lower(arrFloats, 1)..array_upper(arrFloats, 1) LOOP 
    retVal[I] := round(CAST(arrFloats[I] as numeric), roundParam); 
    END LOOP; 
RETURN retVal; 
END; 
$$ 
LANGUAGE plpgsql 
    STABLE 
RETURNS NULL ON NULL INPUT; 

次に、このような何かを呼び出す:

# SELECT array_round(ARRAY[1.53224,0.23411234], 2); 
array_round 
------------- 
{1.53,0.23} 
(1 row) 
0

あなたは、行セットに配列を有効にする必要があります。例えば、generate_seriesを使用して:

SELECT ARRAY(SELECT ROUND(ARRAY[1.53224,0.23411234])[i], 2) FROM generate_series(1,2) AS s(i));  

私はそれはかなり醜いと知っています。このようなマッピングを容易にするヘルパー関数が必要です。

おそらく(はい、それは、恐ろしい遅く、そして脆い動的なコードです)のようなもの:

CREATE OR REPLACE FUNCTION map_with_arg(TEXT, ANYARRAY, TEXT) 
RETURNS ANYARRAY 
IMMUTABLE STRICT 
LANGUAGE 'plpgsql' AS 
$$ 
DECLARE 
    i INTEGER; 
    t TEXT; 
    cmd TEXT; 
BEGIN 
    FOR i IN array_lower($2, 1) .. array_upper($2, 1) LOOP 
     cmd := 'SELECT ('||quote_ident($1)||'('||quote_nullable($2[i])||', '||quote_nullable($3)||'))::TEXT'; 
     EXECUTE cmd INTO t; 
     $2[i] := t; 
    END LOOP; 
    RETURN $2; 
END; 
$$; 

select map_with_arg('repeat', array['can','to']::TEXT[], '2'); 
map_with_arg 
--------------- 
{cancan,toto} 

更新我々が全体のループのために単一の動的ステートメントを使用することができることを私に起こります。これにより、パフォーマンス上の懸念が軽減されます。

CREATE OR REPLACE FUNCTION map_with_arg(TEXT, ANYARRAY, TEXT) 
RETURNS ANYARRAY 
IMMUTABLE STRICT 
LANGUAGE 'plpgsql' AS 
$$ 
DECLARE 
    cmd TEXT; 
    rv TEXT; 
BEGIN 
    cmd := 'SELECT ARRAY(SELECT (' || quote_ident($1)||'($1[i], '||quote_nullable($3)||'))::TEXT FROM generate_subscripts($1, 1) AS gs(i))'; 
    EXECUTE cmd USING $2 INTO rv; 
    RETURN rv; 
END; 
$$; 
+1

ループ内の動的SQLは本当にひどい考えです。 plpgsqlでは実行しないでください。これは相対的な静的言語であり、perl、pythonまたは他のスクリプト言語のパターンはここでは適用できません。 –

+1

はい、それはコードの前に私の文が言うことです。私はplpgsqlに呼び出される関数を渡したいので、この気の利いたこの例でそれを使いました。それはプログラミングの基本的な抽象であり、どの言語でもできることは有益なことです。もちろん、文字列としてそれを行い、新しい文を動的に作成する必要があるのは苦痛ですが、一般的なマッピング関数を記述したい場合は、これを考慮する必要があります。代わりに、100の非一般的なマッピング関数を記述する必要があります。 – Edmund

+0

この抽象化形式が必要な場合は、独自のCモジュールを使用します。 PL/pgSQLは抽象コーディングのための言語ではありません。データベース側の有効でないコードは、パフォーマンスを大幅に低下させる可能性があります。 map_with_argsはC言語で実装された相対的なものでなければなりません –

68

まず、unnestを使用してセットにアレイを回す:

> SELECT n FROM unnest(ARRAY[1.53224,0.23411234]) AS n; 
    n  
------------ 
    1.53224 
0.23411234 
(2 rows) 

次に、カラムに式を適用する:最後

> SELECT ROUND(n, 2) FROM unnest(ARRAY[1.53224,0.23411234]) AS n; 
round 
------- 
    1.53 
    0.23 
(2 rows) 

、バック設定を有効にするarray_aggを使用アレイに:

> SELECT array_agg(ROUND(n, 2)) FROM unnest(ARRAY[1.53224,0.23411234]) AS n; 
    array_agg 
------------- 
{1.53,0.23} 
(1 row) 
+2

そして、それをユーザ定義の 'round(int []、int)'関数にラップすることもできます。 –

+5

これは最も簡潔な解決策です。私はあなたがそれを構築する明確な方法が好きです。 – Edmund

20
postgres=# select array(select round(unnest(array[1.2,2.4,3,4]))); 
    array 
----------- 
{1,2,3,4} 
(1 row) 
+0

ベスト回答は – sudo

+0

'array_agg()'(Joeyの解説を参照)と 'array()'(この解決策)にはいくつかのパフォーマンスの違いがありますか? –

+1

@PeterKrauss私は今ベンチマークを行い、速度は同じです –

関連する問題