web-dev-qa-db-ja.com

postgresqlで「挿入無視」と「重複キー更新」(SQLマージ)をエミュレートする方法は?

一部のSQLサーバーには、主キー/一意キーの制約に違反する場合にINSERTがスキップされる機能があります。たとえば、MySQLにはINSERT IGNOREがあります。

INSERT IGNOREおよびON DUPLICATE KEY UPDATEをPostgreSQLでエミュレートする最良の方法は何ですか?

124
gpilotino

更新を試みてください。行が変更されなかった場合は、行が存在しなかったことを意味するため、挿入を行います。明らかに、これはトランザクション内で行います。

もちろん、クライアント側に余分なコードを配置したくない場合は、これを関数でラップすることができます。また、その思考における非常にまれな競合状態のためのループも必要です。

ドキュメントにこの例があります: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html 、右下の例40-2。

通常、これが最も簡単な方法です。ルールを使用して魔法をかけることもできますが、かなり厄介になる可能性があります。いつでも、関数をラップするアプローチをお勧めします。

これは、単一行または少数行の値に対して機能します。たとえば、サブクエリから大量の行を処理する場合は、INSERTとUPDATEの2つのクエリに分割するのが最適です(もちろん、適切な結合/サブセレクトとして-メインを記述する必要はありません2回フィルター)

31
Magnus Hagander

PostgreSQL 9.5では、これは ネイティブ機能 (数年の間 MySQLが持っていた のように)です:

INSERT ... ON CONFLICT DO NOTHING/UPDATE( "UPSERT")

9.5は、「UPSERT」操作のサポートをもたらします。 INSERTは、ON CONFLICT DO UPDATE/IGNORE句を受け入れるように拡張されています。この句は、重複違反の可能性がある場合に実行する代替アクションを指定します。

...

新しい構文のさらなる例:

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;
143
warren

編集: warren's answer を逃した場合、PG9.5はこれをネイティブに持っています。アップグレードする時!


ビル・カーウィンの答えに基づいて、ルールベースのアプローチがどのように見えるかを説明します(同じDB内の別のスキーマから、複数列の主キーを使用して転送する):

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

注:ルールは、ルールがドロップされるまですべてのINSERT操作に適用されるため、アドホックではありません。

96
EoghanM

insert ignoreロジックを取得するには、以下のようなことができます。リテラル値の選択ステートメントから挿入するだけで最適に動作することがわかったので、NOT EXISTS句を使用して重複キーをマスクできます。重複したロジックの更新を取得するには、pl/pgsqlループが必要だと思います。

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)
23
Keyo

Postgres 9.5以降をお持ちの方は、新しいON CONFLICT DO NOTHING構文が機能するはずです:

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

以前のバージョンを持っている私たちにとって、この正しい結合は代わりに機能します:

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;
23
hanmari
INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')
20
user2342158

PostgreSQLはruleというスキーマオブジェクトをサポートしているようです。

http://www.postgresql.org/docs/current/static/rules-update.html

特定のテーブルにルールON INSERTを作成し、特定の主キー値を持つ行が存在する場合にNOTHINGを実行するか、特定の主キーを持つ行が存在する場合にUPDATEの代わりにINSERTを実行することができます値。

私はこれを自分で試したことがないので、経験から話をしたり、例を挙げたりすることはできません。

12
Bill Karwin

@hanmariが彼のコメントで述べたように。 postgresテーブルに挿入するとき、on conflict(..)do nothingは、重複データを挿入しないために使用するのに最適なコードです。

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

ON CONFLICTのコード行により、insertステートメントでデータの行を挿入できます。クエリと値のコードは、Excelからpostgres dbテーブルに挿入された日付の例です。 IDフィールドが一意であることを確認するために使用するpostgresテーブルに制約を追加しています。同じデータ行で削除を実行する代わりに、1から始まるID列の番号を付け直すsqlコードの行を追加します。例:

q = 'ALTER id_column serial RESTART WITH 1'

データにIDフィールドがある場合、これをプライマリID /シリアルIDとして使用せず、ID列を作成してシリアルに設定します。この情報が皆さんの役に立つことを願っています。 *私は大学でソフトウェア開発/コーディングの学位を取得していません。コーディングで知っていることはすべて自分で勉強します。

3
Yankeeownz

このソリューションは、ルールの使用を回避します。

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

ただし、パフォーマンス上の欠点があります( PostgreSQL.org を参照)。

EXCEPTION句を含むブロックは、ブロックを持たないブロックよりも出入りが非常に高価です。したがって、必要なくEXCEPTIONを使用しないでください。

2
NumberFour

一括して、挿入前にいつでも行を削除できます。存在しない行を削除してもエラーは発生しないため、安全にスキップされます。

1
David Noriega