C'est le problème récurrent de SELECT or INSERT
, lié à (mais différent de) un UPSERT. La nouvelle fonctionnalité UPSERT dans Postgres 9.5 est toujours essentielle.
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 cette façon, vous n'écrivez pas réellement une nouvelle version de ligne sans besoin.
Cependant , il y a encore un petit cas d'angle pour une condition de concurrence . Les transactions simultanées peuvent avoir ajouté une ligne en conflit, qui n'est pas encore visible dans la même instruction. Puis INSERT
et SELECT
venir vide.
Solution appropriée pour UPSERT à une seule ligne :
- SELECT ou INSERT est-il dans une fonction sujette à des conditions de concurrence ?
Solutions générales pour UPSERT en masse :
- Comment utiliser RETURNING avec ON CONFLICT dans PostgreSQL ?
Sans charge d'écriture simultanée
Si les écritures simultanées (à partir d'une session différente) ne sont pas possibles, vous n'avez pas besoin de verrouiller la ligne et vous pouvez simplifier :
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;