web-dev-qa-db-ja.com

ON CONFLICT DO NOTHINGと同等のUPDATE postgres

更新されたバージョンが主キー制約に違反しない場合、postgresデータベースの行を更新したいと思います。もしそうなら、私は行をそのまま残したいと思います。

次のようなクエリを実行した場合、テーブルにcol1, col2, col3の主キーがあると仮定します。

UPDATE table SET (col1, col2) = ('AAA', 'BBB') 
      WHERE col1='AAB' AND col2='BBA';

2つのエントリが存在する場合、クエリは失敗し、重複キーエラーが発生します。

'AAA', 'BBB', 'CCC'
'AAB', 'BBA', 'CCC'

つまり、col3は、既存の行と更新される行で同じです。

行をINSERTingしている場合はON CONFLICT DO NOTHINGを使用しますが、UPDATEの実装を見つけることができません。同等のものはありますか?

4
BHC

私の知る限り、そのような同等のものはありません。

Postgresqlデータベースに接続するアプリケーションを開発しているとしましょう。質問のコンテキストで、覚えておくべきことがいくつかあります。

  • 直感に反するかもしれませんが、DBによってスローされたエラーをgoodと見なす必要があります。
    これはステータスを取得するためのものであり、アプリケーションのクラッシュを意味するものではありません。
  • 挿入の場合、アクション_on conflict_(更新またはなし)の代替選択肢があるため、ユーザーが決定できる構文を使用することは理にかなっています。
    更新の場合、できることは...何もありません。
    それでは、SQLに選択の余地がないのに、なぜ特定のことを実行するように要求するのでしょうか。 DBレポートエラーはgoodであることを忘れないでください。そのため、DBは何もせず、理由を説明します。
  • 最後に、主キーを更新することは悪い習慣です。
    挿入の_ON CONFLICT ..._は、主キーフィールドを更新するためのものではありません。実際は正反対です。単一のレコード内のすべてのフィールド主キーからのものを除くを更新することを目的としています。

その点については、クエリが失敗するために主キーの競合は必要なかったことに注意してください
「便利な」_ON UPDATE NO ACTION_外部キーを持つ1つのレコードも失敗します(これは、_ON UPDATE CASCADE_ ...で50のテーブルの10M +レコードを更新するよりも優れています)。ところで、Oracleには_ON UPDATE CASCADE_句さえないことをご存知ですか?その理由は何だと思いますか?


そのような状況で何ができる/すべきでないでしょうか?

  1. 主キーを更新しないでください、私が言ったように。あなたの質問はUNIQUE制約に対しても有効ですが、主キーを更新しないでください。
  2. 競合するレコードが既に存在するかどうか確認しないでください。時間がかかり、信頼性が低下する場合があります。
    エラーコードを回避するためだけに、何百万ものレコードを選択しますか?
    また、他の制約(CHECKまたはEXCLUSION)に拡張する場合、もう一度、回避するためだけに、エラーなしで必要な追加コードを実際に入力しますか?エラーコード?
    最後に、行レベルのセキュリティを実装した場合、見えないレコードから競合が発生する可能性があります。
  3. アプリのエラーコードを処理する。受信ステータスは[〜#〜] good [〜#〜]です。
  4. トランザクションの途中である場合はセーブポイントを使用します
    これは、DBエラーの唯一の厄介なことです。トランザクションの途中でエラーが発生した場合、すべてに対して_current transaction is aborted, commands ignored until end of transaction block_を取得し始めます。
    できれば、トランザクション全体をロールバックして、すべてを最初からやり直す必要はありません。次のコードを使用して回避できます。

どうぞ:

_BEGIN;
SAVEPOINT MySavepoint;
UPDATE mytable set myuniquefield = 3; /*2+ records are going to be updated */
rollback to savepoint MySavepoint;
/*Insert Some more queries here*/
COMMIT;
_
5
FXD

WHERE NOT EXISTS句を使用して相関サブクエリを使用すると、次のように、更新によって重複が生成されないようにすることができます。

UPDATE mytable t
SET (col1, col2) = ('AAA', 'BBB')
WHERE t.col1 = 'AAB' and t.col2 = 'BBA'
AND NOT EXISTS (
   SELECT 1 FROM mytable WHERE col1 = 'AAA' AND col2 = 'BBB' AND col3 = t.col3
);

this db fiddle でテストされています。

[〜#〜]編集[〜#〜]

Roman Konovalがコメントしたように、UPDATEの実行中に同時トランザクションが同じキーを挿入すると、重複キーエラーが引き続き発生することに注意してください。これは、テーブルの主キーを更新することは良い習慣ではないことを正確に示しています(この問題の詳細については、@ Lauからの以下の回答を参照してください)。

3
GMB