1

dataという名前のテーブルに挿入したいアイテムを含む文字列ベクトルがありますfoos。の要素の一部がdataテーブルに既に存在する可能性があるため、それらに注意する必要があります。

私が使用しているソリューションは、dataベクトルを仮想テーブルに変換することから始まりますold_and_newold次に、 に既に存在する要素を含む仮想テーブルを作成しfoosます。new次に、本当に新しい要素で仮想テーブルを構築します。最後に、新しい要素を table に挿入しますfoos

WITH   old_and_new AS (SELECT unnest ($data :: text[]) AS foo),
       old AS (SELECT foo FROM foos INNER JOIN old_and_new USING (foo)),
       new AS (SELECT * FROM old_and_new EXCEPT SELECT * FROM old)
INSERT INTO foos (foo) SELECT foo FROM new

これは非並行設定では正常に機能しますが、並行スレッドが同じ新しい要素を同時に挿入しようとすると失敗します。分離レベルを に設定することでこれを解決できることはわかっていますがserializable、それは非常に手間がかかります。

この問題を解決できる他の方法はありますか?INSERTエラーを無視しても安全であることをPostgreSQLに伝える方法があれば...

4

3 に答える 3

3

この問題を解決できる他の方法はありますか?

たくさんありますが、どれも万能薬ではありません...

select for update行がまだ存在しないため、 のように挿入をロックすることはできません。

テーブル全体をロックすることもできますが、トランザクションをシリアル化するよりもさらに手間がかかります。

アドバイザリ ロックを使用できますが、デッドロックには十分注意してください。一貫性のある予測可能な順序でロックを取得できるように、新しいキーを並べ替えます。(PG のソース コードに詳しい人が参加してくれることを期待していますが、シリアル化可能な分離レベルで使用される述語ロックは、まさにそれを行うことになると思います。)

純粋な sql では、do ステートメントを使用して行を 1 つずつループし、発生したエラーをトラップすることもできます。

同様に、複雑なupsert関数を作成して、データごとに1回呼び出すことができます...

アプリ レベルで $data を作成している場合は、挿入を 1 つずつ実行してエラーを無視できます。

そして、私はいくつかの追加オプションを忘れたと確信しています...

于 2013-05-03T14:01:58.180 に答える
0

私はアーウィンとデニスの両方の答えが好きですが、別のアプローチは、同時セッションでネスト解除と別の一時テーブルへのロードを実行し、オプションでターゲットテーブルに対して可能な重複を排除し、この一時から単一のセッションを選択することです。一時テーブルの内部重複を適切な方法で解決し、既存の値を再度確認してターゲット テーブルに挿入し、選択した一時テーブル レコードを削除します (共通テーブル式を使用した同じクエリ内)。

これは、データ ウェアハウスの抽出 - ロード - 変換パラダイムのスタイルで、よりバッチ指向になりますが、一意の制約の問題を処理する必要がないことが保証されます。

最終的な挿入をデータ収集から分離する (利点の可能性)、一時テーブルを頻繁にバキュームする必要がある (欠点の可能性がある) など、その他の利点/欠点が適用されます。同じ状況で。

于 2013-05-04T07:35:34.497 に答える