upsertで競合する行のIDを取得する方法は?


18

tag2つの列を持つテーブルがあります:id(uuid)とname(text)。テーブルに新しいタグを挿入したいのですが、タグが既に存在する場合idは、既存のレコードを取得するだけです。

私はちょうどON CONFLICT DO NOTHINGと組み合わせて使用できると仮定しましたRETURNING "id"

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";

ただし、「foo」という名前のタグがすでに存在する場合、これは空の結果セットを返します。

次に、noop DO UPDATE句を使用するようにクエリを変更しました。

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";

これは意図したとおりに機能しますが、名前を既存の値に設定しているだけなので、やや混乱します。

これはこの問題を解決する方法ですか、それとも私が見逃しているより簡単なアプローチがありますか?


試しましたreturning excluded.idか?
a_horse_with_no_name

@a_horse_with_no_nameこれはERROR: missing FROM-clause entry for table "excluded"、を使用するときにちょうど私に与えますDO NOTHING
デアホッホシュプラー

回答:


8

挿入する値がすべて新しいか、テーブルまたはミックスに既にある場合、これは(テストした限り)3つのケースすべてで機能します。

WITH
  val (name) AS
    ( VALUES                          -- rows to be inserted
        ('foo'),
        ('bar'),
        ('zzz')
    ),
  ins AS
    ( INSERT INTO
        tag (name)
      SELECT name FROM val
      ON CONFLICT (name) DO NOTHING
      RETURNING id, name              -- only the inserted ones
    )
SELECT COALESCE(ins.id, tag.id) AS id, 
       val.name
FROM val
  LEFT JOIN ins ON ins.name = val.name
  LEFT JOIN tag ON tag.name = val.name ;

おそらく新しいON CONFLICT構文を使用せずに、これを行う他の方法がおそらくあるでしょう。


4

これがどのように実行されるかはわかりませんが、試してみるための別のオプションとして、ここでは同じように古い方法を実行しています(なしON CONFLICT):

WITH items (name) AS (VALUES ('foo'), ('bar'), ('zzz')),
     added        AS
      (
        INSERT INTO tag (name)

        SELECT name FROM items
        EXCEPT
        SELECT name FROM tag

        RETURNING id
      )
SELECT id FROM added

UNION ALL

SELECT id FROM tag
WHERE name IN (SELECT name FROM items)
;

つまり、tagテーブルにない[一意の]名前のみを挿入し、IDを返します。tag最終出力のために、それをに存在する名前のIDと組み合わせます。ypercubeᵀᴹが示唆するnameように、出力にスローすることもできます。これにより、どのIDがどの名前に一致するかがわかります。


1
はい。私のコードの最後のSELECTは、のように書くこともできるSELECT .. FROM ins UNION ALL SELECT ... FROM val JOIN tag ... ;
ypercubeᵀᴹ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.