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;