これは、よくある問題と密接に関連する、「SELECT-or-INSERT」の繰り返しの問題UPSERT
です。今後のPostgres 9.5 ではINSERT .. ON CONFLICT DO NOTHING | UPDATE
、それぞれにクリーンなソリューションを提供するための新しい機能が提供されます。
Postgres 9.4 の実装
今のところ、サーバー側の plpgsql 関数を 2 つ使用するこの防弾実装をお勧めします。のヘルパー関数だけがINSERT
より高価なエラー トラップを実装し、それSELECT
が成功しない場合にのみ呼び出されます。
これにより、一意の違反が原因で例外が発生することはなく、常に行が返されます。
仮定:
データ型tbl
の列で名前が付けられたテーブルを想定しています。それに応じてケースに適応します。x
text
x
UNIQUE
またはが定義されてPRIMARY KEY
います。
基礎となるテーブル ( ) から行全体を返す必要がありますreturn a record (found or created)
。
多くの場合、行はすでに存在します。(大部分のケースである必要はありませSELECT
ん。INSERT
INSERT
ヘルパー関数:
CREATE OR REPLACE FUNCTION f_insert_x(_x text)
RETURNS SETOF tbl AS
$func$
BEGIN
RETURN QUERY
INSERT INTO tbl(x) VALUES (_x) RETURNING *;
EXCEPTION WHEN UNIQUE_VIOLATION THEN -- catch exception, no row is returned
-- do nothing
END
$func$ LANGUAGE plpgsql;
主な機能:
CREATE OR REPLACE FUNCTION f_x(_x text)
RETURNS SETOF tbl AS
$func$
BEGIN
LOOP
RETURN QUERY
SELECT * FROM tbl WHERE x = _x
UNION ALL
SELECT * FROM f_insert_x(_x) -- only executed if x not found
LIMIT 1;
EXIT WHEN FOUND; -- else keep looping
END LOOP;
END
$func$ LANGUAGE plpgsql;
電話:
SELECT * FROM f_x('foo');
SQL フィドルのデモ。
この関数は、この関連する回答で私が解決したことに基づいています:
詳細な説明とリンクがあります。
また、ポリモーフィックな戻り値の型と動的 SQL を使用して汎用関数を作成し、特定の列とテーブルで機能することもできます (ただし、それはこの質問の範囲を超えています) 。
UPSERT
Craig Ringerによるこの関連する回答の基本: