単一のSQLステートメントのソリューション。 PostgreSQLが必要8.4 またはそれ以降。
次のデモを検討してください:
テストセットアップ:
CREATE TEMP TABLE tbl (
id serial PRIMARY KEY
,txt text UNIQUE -- obviously there is unique column (or set of columns)
);
INSERT INTO tbl(txt) VALUES ('one'), ('two');
INSERT / SELECTコマンド:
WITH v AS (SELECT 'three'::text AS txt)
,s AS (SELECT id FROM tbl JOIN v USING (txt))
,i AS (
INSERT INTO tbl (txt)
SELECT txt
FROM v
WHERE NOT EXISTS (SELECT * FROM s)
RETURNING id
)
SELECT id, 'i'::text AS src FROM i
UNION ALL
SELECT id, 's' FROM s;
-
最初のCTEv 厳密には必要ではありませんが、値を入力する必要があります 一度だけ。
-
2番目のCTEが選択
id
tbl
から 「行」が存在する場合。 -
3番目のCTEi挿入
tbl
への「行」 存在しない場合(そして存在する場合のみ)、id
を返します 。 -
最後の
SELECT
id
を返します 。src
列を追加しました 「ソース」を示します-「行」が既存であり、id
であるかどうか SELECTからのものであるか、「行」が新しく、id
も新しい 。 -
このバージョンは、
tbl
からの追加のSELECTを必要としないため、可能な限り高速である必要があります。 代わりにCTEを使用します。
マルチユーザー環境で発生する可能性のある競合状態に対してこれを安全にするには:
また、Postgresの新しいUPSERTを使用した最新の技術 9.5 以降:
- 関数内のSELECTまたはINSERTは競合状態になりやすいですか?