È il problema ricorrente di SELECT or INSERT
, relativo a (ma diverso da) un UPSERT. La nuova funzionalità UPSERT in Postgres 9.5 è ancora fondamentale.
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;
In questo modo non scrivi effettivamente una nuova versione di riga senza bisogno.
Tuttavia , c'è ancora un piccolo caso d'angolo per una condizione di gara . Le transazioni simultanee potrebbero aver aggiunto una riga in conflitto, che non è ancora visibile nella stessa dichiarazione. Quindi INSERT
e SELECT
torna vuoto.
Soluzione adeguata per UPSERT a riga singola:
- SELEZIONA o INSERT in una funzione soggetta a condizioni di gara?
Soluzioni generali per UPSERT in blocco:
- Come utilizzare RETURNING con ON CONFLICT in PostgreSQL?
Senza carico di scrittura simultaneo
Se le scritture simultanee (da una sessione diversa) non sono possibili non è necessario bloccare la riga e puoi semplificare:
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;