議論のために簡単な表があります。 idを選択してそれらをループする_loop_test
_という関数があります。 IDの配列を選択してループし、トランザクションに変更を加えることができます。
_CREATE OR REPLACE FUNCTION loop_test() RETURNS void AS $$
DECLARE
_ids_array INTEGER[];
_id INTEGER;
BEGIN
SELECT ARRAY(SELECT id FROM loop_test) INTO _ids_array;
FOREACH _id IN ARRAY _ids_array
LOOP
UPDATE loop_test SET looped = TRUE WHERE id = _id;
END LOOP;
END;
$$ LANGUAGE plpgsql;
_
テーブル:
_db=# \d loop_test;
Table "public.loop_test"
Column | Type | Modifiers
---------------+---------+-----------
id | integer |
other_id | integer |
id_copy | integer |
other_id_copy | integer |
looped | boolean |
db=# select * from loop_test;
id | other_id | id_copy | other_id_copy | looped
----+----------+---------+---------------+--------
1 | 10 | | |
6 | 15 | | |
2 | 11 | | |
7 | 16 | | |
3 | 12 | | |
4 | 13 | | |
5 | 14 | | |
(7 rows)
_
select loop_test()
を呼び出すと、次の結果が得られます。
_db=# select * from loop_test;
id | other_id | id_copy | other_id_copy | looped
----+----------+---------+---------------+--------
1 | 10 | | | t
6 | 15 | | | t
2 | 11 | | | t
7 | 16 | | | t
3 | 12 | | | t
4 | 13 | | | t
5 | 14 | | | t
(7 rows)
_
ただし、id
と_other_id
_の両方を選択して配列にする関数を作成したいと思います。 _agg_array
_のようなものを使用するように言われました、しかし私はそれがどのように機能するか完全には理解していません。
私は次のようなものを想像していましたか?
_CREATE OR REPLACE FUNCTION agg_loop_test() RETURNS void AS $$
DECLARE
_ids_array INTEGER[][];
_id INTEGER;
BEGIN
SELECT AGG_ARRAY(SELECT id, other_id FROM loop_test) INTO _ids_array;
FOREACH _id IN ARRAY _ids_array
LOOP
UPDATE loop_test SET id_copy = _id[0], other_id_copy = _id[1] WHERE id = _id[0];
END LOOP;
END;
$$ LANGUAGE plpgsql;
_
A muchより良い方法ですが、更新するだけです。ループは必要ありません。
UPDATE loop_test
SET id_copy = id
, other_id_copy = other_id;
WHERE id IS NOT NULL;
WHERE
条件は、id
がnullになる可能性があり、完全に同等のものが必要な場合にのみ役立ちます。
ループを探索しているだけなら、複数の変数を割り当てることができます。
CREATE OR REPLACE FUNCTION better_loop_test()
RETURNS void AS
$func$
DECLARE
_id int;
_other_id int;
BEGIN
-- example makes no sense, just a loop demo
FOR _id, _other_id IN
SELECT id, other_id FROM loop_test
LOOP
UPDATE loop_test
SET id_copy = _id
, other_id_copy = _other_id
WHERE id = _id;
END LOOP;
END
$func$ LANGUAGE plpgsql;
既知のタイプの2つの列が必要なだけですが、行全体(場合によっては大きい)をフェッチするよりも少し安くなる可能性があります。
@アーウィンの返答は完全に正しいです。説明されている例で配列を使用すると、パフォーマンスエラーが発生します(残念ながら一般的です)。一部の値を関数パラメーターとして渡す必要があるため、必要になる場合があります。
2つの手法があります。1。複合値の配列を渡す、2。多次元配列を渡す。パフォーマンスは+/-で同じである必要があります-コンポジットの配列を使用する方が読みやすい場合があります。 9.3のクエリ結果から多次元配列を作成できるかどうかはわかりません。
CREATE TYPE test_type AS (id1 int, id2 int);
CREATE OR REPLACE FUNCTION fx1(ids test_type[])
RETURNS void AS $$
DECLARE r test_type;
FOR r IN ARRAY ids
LOOP
UPDATE ...
END LOOP;
おそらくまだ、関数UPDATE
で循環せずに使用できるunnest
ステートメントは1つだけです。
CREATE TABLE test (id1 integer, id2 integer);
UPDATE test SET id2 = u.id2
FROM unnest(array[(1,10),(3,4)]::test_type[]) u
WHERE test.id1 = u.id1;
パフォーマンスへの影響は、アレイのサイズに依存します-小さなアレイの場合は最小限になりますが、それでもサイクルのネストが深くなる可能性があり、パフォーマンスの問題が発生する可能性があります。
多次元の場合 arrays PLpgSQL FOREACH
ステートメントにはSLICE
句があります:
CREATE OR REPLACE FUNCTION fx2(ids int[])
RETURNS void AS $$
DECLARE _ids int[];
BEGIN
FOREACH _ids SLICE 1 IN ARRAY ids
LOOP
RAISE NOTICE 'ids[0]=% ids[1]=%', _ids[0], _ids[1];
END LOOP;
END;
$$ LANGUAGE plpgsql;
postgres=# SELECT fx2(ARRAY[[1,2],[3,4]]);
NOTICE: ids[0]=<NULL> ids[1]=1
NOTICE: ids[0]=<NULL> ids[1]=3
多次元配列については知りませんが、私がやろうとしていたことを実行するはるかに良い方法を見つけました:
CREATE OR REPLACE FUNCTION better_loop_test() RETURNS void AS $$
DECLARE
_row RECORD;
BEGIN
FOR _row IN SELECT * FROM loop_test LOOP
UPDATE loop_test SET id_copy = _row.id, other_id_copy = _row.other_id WHERE id = _row.id;
END LOOP;
END;
$$ LANGUAGE plpgsql;