sql >> データベース >  >> RDS >> PostgreSQL

RETURNING from INSERT ...ONCONFLICTに除外された行を含める方法

    発生するエラー:

    ON CONFLICTDOUPDATEコマンドは行に2回影響を与えることはできません

    1つのコマンドで同じ行を複数回アップサートしようとしていることを示します。言い換えれば、(name, url, email)に重複があります VALUESで リスト。複製を折りたたむと(それがオプションの場合)、機能するはずです。ただし、デュープの各セットからどの行を選択するかを決定する必要があります。

    INSERT INTO feeds_person (created, modified, name, url, email)
    SELECT DISTINCT ON (name, url, email) *
    FROM  (
       VALUES
       ('blah', 'blah', 'blah', 'blah', 'blah')
       -- ... more
       ) v(created, modified, name, url, email)  -- match column list
    ON     CONFLICT (name, url, email) DO UPDATE
    SET    url = feeds_person.url
    RETURNING id;
    

    自立型のVALUESを使用しているため ここで、式をデフォルト以外の型に明示的な型キャストを追加する必要があります。いいね:

    VALUES
        (timestamptz '2016-03-12 02:47:56+01'
       , timestamptz '2016-03-12 02:47:56+01'
       , 'n3', 'u3', 'e3')
       ...
    

    あなたのtimestamptz 列には明示的な型キャストが必要ですが、文字列型はデフォルトのtextで動作できます 。 (引き続きvarchar(n)にキャストできます すぐに。)

    複製の各セットからどの行を選択するかを決定する方法があります:

    • 各GROUPBYグループの最初の行を選択しますか?

    そうです、(現在)除外する方法はありません RETURNINGの行 句。 Postgres Wikiを引用します:

    RETURNINGに注意してください 「EXCLUDED.*」は表示されません "UPDATEからのエイリアス (一般的な「TARGET.* 「エイリアスはそこに表示されます。そうすることで、単純で一般的なケース[30]に厄介なあいまいさが生じ、ほとんどまたはまったくメリットがないと考えられます。将来のある時点で、if RETURNINGを公開する方法を追求する可能性があります。 -投影されたタプルが挿入および更新されましたが、これはおそらく、機能の最初のコミットされた反復に含める必要はありません[31]。

    ただし 、更新されるはずのない行を更新するべきではありません。空の更新は通常の更新とほぼ同じくらい費用がかかり、意図しない副作用が発生する可能性があります。そもそもUPSERTは厳密には必要ありません。ケースは、「SELECTまたはINSERT」のように見えます。関連:

    • 関数内のSELECTまたはINSERTは競合状態になりやすいですか?

    1つ 行のセットを挿入するよりクリーンな方法は、データ変更CTEを使用することです:

    WITH val AS (
       SELECT DISTINCT ON (name, url, email) *
       FROM  (
          VALUES 
          (timestamptz '2016-1-1 0:0+1', timestamptz '2016-1-1 0:0+1', 'n', 'u', 'e')
        , ('2016-03-12 02:47:56+01', '2016-03-12 02:47:56+01', 'n1', 'u3', 'e3')
          -- more (type cast only needed in 1st row)
          ) v(created, modified, name, url, email)
       )
    , ins AS (
       INSERT INTO feeds_person (created, modified, name, url, email)
       SELECT created, modified, name, url, email FROM val
       ON     CONFLICT (name, url, email) DO NOTHING
       RETURNING id, name, url, email
       )
    SELECT 'inserted' AS how, id FROM ins  -- inserted
    UNION  ALL
    SELECT 'selected' AS how, f.id         -- not inserted
    FROM   val v
    JOIN   feeds_person f USING (name, url, email);
    

    追加された複雑さは、INSERTがある大きなテーブルに支払う必要があります ルールであり、SELECT 例外。

    もともと、私はNOT EXISTSを追加していました 最後のSELECTの述語 結果の重複を防ぐため。しかし、それは冗長でした。 単一のクエリのすべてのCTEは、テーブルの同じスナップショットを参照します。 ON CONFLICT (name, url, email) DO NOTHINGで返されたセット INNER JOINの後に返されるセットに対して相互に排他的です 同じ列にあります。

    残念ながら、これは競合状態の小さなウィンドウも開きます 。もし...

    • 同時トランザクションにより、競合する行が挿入されます
    • まだコミットしていません
    • しかし最終的にはコミットします

    ...一部の行が失われる可能性があります。

    INSERT .. ON CONFLICT DO NOTHING 、その後に別のSELECT これを克服するために、同じトランザクション内ですべての行をクエリします。これにより、競合状態の別の小さなウィンドウが開きます。 並行トランザクションがINSERTの間にテーブルへの書き込みをコミットできるかどうか およびSELECT (デフォルトではREAD COMMITTED 分離レベル)。 REPEATABLE READで回避できます トランザクション分離(またはより厳密)。または、テーブル全体に(おそらく高価または許容できない)書き込みロックを使用します。あなたはあなたが必要とするどんな行動も得ることができます、しかし支払うべき代償があるかもしれません。

    関連:

    • PostgreSQLでONCONFLICTを使用してRETURNINGを使用するにはどうすればよいですか?
    • 更新せずに、ONCONFLICTを使用してINSERTから行を返します



    1. OracleのCOALESCE()関数

    2. 複数のクエリに対するPDOのサポート(PDO_MYSQL、PDO_MYSQLND)

    3. Javaでユーザー定義型を含むOracleストアドプロシージャを呼び出す方法は?

    4. SETNAMESを使用するかどうか