PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Gibt Zeilen von INSERT mit ON CONFLICT zurück, ohne dass eine Aktualisierung erforderlich ist

Es ist das wiederkehrende Problem von SELECT or INSERT , verwandt (aber anders als) ein UPSERT. Die neue UPSERT-Funktionalität in Postgres 9.5 ist immer noch maßgeblich.

WITH ins AS (
   INSERT INTO names(name)
   VALUES ('bob')
   ON     CONFLICT ON CONSTRAINT names_name_key DO UPDATE
   SET    name = NULL
   WHERE  FALSE      -- never executed, but locks the row
   RETURNING id
   )
SELECT id FROM ins
UNION  ALL
SELECT id FROM names
WHERE  name = 'bob'  -- only executed if no INSERT
LIMIT  1;

Auf diese Weise schreiben Sie nicht unnötigerweise eine neue Zeilenversion.

Allerdings , gibt es immer noch einen winzigen Eckfall für eine Rennbedingung . Gleichzeitige Transaktionen haben möglicherweise eine widersprüchliche Zeile hinzugefügt, die noch nicht in derselben Anweisung sichtbar ist. Dann INSERT und SELECT leer ausgehen.

Richtige Lösung für einreihigen UPSERT:

  • Ist SELECT oder INSERT in einer Funktion anfällig für Race-Conditions?

Allgemeine Lösungen für Massen-UPSERT:

  • Wie verwendet man RETURNING mit ON CONFLICT in PostgreSQL?

Ohne gleichzeitige Schreiblast

Wenn gleichzeitige Schreibvorgänge (aus einer anderen Sitzung) nicht möglich sind, müssen Sie die Zeile nicht sperren und können Folgendes vereinfachen:

WITH ins AS (
   INSERT INTO names(name)
   VALUES ('bob')
   ON     CONFLICT ON CONSTRAINT names_name_key DO NOTHING  -- no lock needed
   RETURNING id
   )
SELECT id FROM ins
UNION  ALL
SELECT id FROM names
WHERE  name = 'bob'  -- only executed if no INSERT
LIMIT  1;