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;