8

a値のコレクションにa=bを設定して、一括更新を行いたいとします。これは、一連のクエリで簡単に実行できます。UPDATE

UPDATE foo SET value='foo' WHERE id=1
UPDATE foo SET value='bar' WHERE id=2
UPDATE foo SET value='baz' WHERE id=3

しかし今、私はこれを大量にやりたいと思います。ID と新しい値を含む 2 次元配列があります。

[ [ 1, 'foo' ]
  [ 2, 'bar' ]
  [ 3, 'baz' ] ]

これら 3 つの UPDATE を 1 つの SQL クエリで効率的に行う方法はありますか?

私が検討したいくつかの解決策:

  1. 一時テーブル

    CREATE TABLE temp ...;
    INSERT INTO temp (id,value) VALUES (....);
    UPDATE foo USING temp ...
    

    しかし、これは実際には問題を動かすだけです。一括 INSERT を実行する方が簡単かもしれませんが (または、少なくとも見苦しくはありません)、少なくとも 3 つのクエリが必要です。

  2. データ ペアを SQL 配列として渡すことにより、入力を非正規化します。ただし、これによりクエリが非常に醜くなります

    UPDATE foo
    USING (
        SELECT
            split_part(x,',',1)::INT AS id,
            split_part(x,',',2)::VARCHAR AS value
        FROM (
            SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz']) AS x
        ) AS x;
    )
    SET value=x.value WHERE id=x.id
    

    これにより、単一のクエリを使用できるようになりますが、そのクエリは見苦しく、非効率的になります (特に混合データ型や複雑なデータ型の場合)。

より良い解決策はありますか?または、複数の UPDATE クエリに頼る必要がありますか?

4

1 に答える 1

8

table通常、マージを簡単にするために、十分なインデックスを使用してからバッチ更新する必要があります。

CREATE TEMP TABLE updates_table
        ( id integer not null primary key
        , val varchar
        );
INSERT into updates_table(id, val) VALUES
 ( 1, 'foo' ) ,( 2, 'bar' ) ,( 3, 'baz' )
        ;

UPDATE target_table t
SET value = u.val
FROM updates_table u
WHERE t.id = u.id
        ;

したがって、おそらく update_table に次のようなものを入力する必要があります。


INSERT into updates_table(id, val)
SELECT
        split_part(x,',',1)::INT AS id,
        split_part(x,',',2)::VARCHAR AS value
    FROM (
        SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz']) 
         ) AS x
     ;

id注意: のフィールドのインデックス (または主キー)updates_tableは重要です。(しかし、このような小さなセットの場合、オプティマイザーによってハッシュジョインが選択される可能性があります)


さらに、更新の場合、同じ値での更新を避けることが重要です。これにより、余分な行バージョンが作成されVACUUM、さらに更新がコミットされた後に結果として生じるアクティビティが発生します。

UPDATE target_table t
    SET value = u.val
    FROM updates_table u
    WHERE t.id = u.id
    AND (t.value IS NULL OR t.value <> u.value)
            ;
于 2015-02-25T15:57:52.197 に答える