sql >> Base de Datos >  >> RDS >> PostgreSQL

Devolver filas de INSERT con ON CONFLICT sin necesidad de actualizar

Es el problema recurrente de SELECT or INSERT , relacionado con (pero diferente de) un UPSERT. La nueva funcionalidad UPSERT en Postgres 9.5 sigue siendo fundamental.

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;

De esta manera, en realidad no escribe una nueva versión de fila sin necesidad.

Sin embargo , todavía hay un pequeño caso de esquina para una condición de carrera . Las transacciones simultáneas pueden haber agregado una fila en conflicto, que aún no está visible en la misma declaración. Luego INSERT y SELECT vienen vacíos.

Solución adecuada para UPSERT de una sola fila:

  • ¿Es SELECCIONAR o INSERTAR en una función propensa a condiciones de carrera?

Soluciones generales para UPSERT masivo:

  • ¿Cómo usar RETURNING con ON CONFLICT en PostgreSQL?

Sin carga de escritura simultánea

Si las escrituras simultáneas (desde una sesión diferente) no son posibles, no necesita bloquear la fila y puede simplificar:

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;