Det er det tilbagevendende problem med SELECT or INSERT
, relateret til (men forskellig fra) en UPSERT. Den nye UPSERT-funktionalitet i Postgres 9.5 er stadig medvirkende.
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;
På denne måde skriver du faktisk ikke en ny rækkeversion uden behov.
Men , er der stadig en lille hjørnekasse til en løbstilstand . Samtidige transaktioner kan have tilføjet en modstridende række, som endnu ikke er synlig i samme opgørelse. Derefter INSERT
og SELECT
komme op tom.
Korrekt løsning til enkeltrækket UPSERT:
- Er SELECT eller INSERT i en funktion, der er tilbøjelig til løbsforhold?
Generelle løsninger til bulk UPSERT:
- Hvordan bruger man RETURNING med ON CONFLICT i PostgreSQL?
Uden samtidig skrivebelastning
Hvis samtidige skrivninger (fra en anden session) ikke er mulige, behøver du ikke låse rækken og kan forenkle:
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;