Ce face ON CONFLICT DO UPDATE (UPSERT)
Operația de UPSERT (Update or Insert) este implementată în PostgreSQL prin clauza ON CONFLICT atașată comenzii INSERT. Dacă rândul nou încalcă o constrângere UNIQUE sau PRIMARY KEY, PostgreSQL poate fie să ignore inserarea (DO NOTHING), fie să actualizeze rândul existent (DO UPDATE) în mod garantat atomic, fără condiții de cursă (race conditions).
În interiorul clauzei DO UPDATE SET, tabelul virtual special EXCLUDED conține valorile care ar fi fost inserate dacă nu apărea conflictul, iar numele tabelului țintă se referă la valorile deja existente în baza de date.
Clauze, operatori și argumente
| Clauză / Operator | Descriere |
|---|---|
ON CONFLICT (coloane) |
Specifică coloana sau setul de coloane care au un index unic sau o cheie primară pe care se detectează conflictul. |
ON CONFLICT ON CONSTRAINT nume |
Identifică explicit constrângerea unică după numele ei. |
DO NOTHING |
Ignoră silențios rândurile care ar produce o eroare de cheie duplicată. |
DO UPDATE SET col = ... |
Actualizează rândul existent în caz de conflict. |
EXCLUDED.coloana |
Referință la valoarea propusă spre inserare pentru coloana respectivă. |
WHERE conditie (în DO UPDATE) |
Efectuează actualizarea doar dacă este îndeplinită condiția (altfel lasă rândul neschimbat). |
Exemple
Exemplul 1: Actualizarea stocului și a prețului cu ON CONFLICT DO UPDATE și EXCLUDED
Dacă produsul cu sku-ul dat nu există, este inserat; dacă există deja, îi cumulăm stocul și îi actualizăm prețul doar dacă s-a modificat.
INSERT INTO stoc_produse (sku, stoc, pret_unitar, actualizat_la)
VALUES ('SSD-1TB-PRO', 25, 419.00, NOW())
ON CONFLICT (sku) DO UPDATE
SET stoc = stoc_produse.stoc + EXCLUDED.stoc,
pret_unitar = EXCLUDED.pret_unitar,
actualizat_la = NOW()
RETURNING sku, stoc, pret_unitar;
Rezultat interogare
sku | stoc | pret_unitar
-------------+------+-------------
SSD-1TB-PRO | 40 | 419.00
(1 row)
INSERT 0 1
Exemplul 2: Inserare idempotentă cu ON CONFLICT DO NOTHING
Inserăm o listă de abonați la newsletter și ignorăm adresele de e-mail care există deja în tabel, fără ca tranzacția să eșueze.
INSERT INTO abonati_newsletter (email, sursa)
VALUES
('alex@exemplu.ro', 'landing_page'),
('diana@exemplu.ro', 'checkout')
ON CONFLICT (email) DO NOTHING
RETURNING id, email;
Rezultat interogare
id | email
-----+-------------------
308 | diana@exemplu.ro
(1 row)
INSERT 0 1
Greșeli frecvente
- Folosirea
ON CONFLICT (coloana)pe o coloană care nu are o constrângereUNIQUEsauPRIMARY KEY: PostgreSQL va returna eroareathere is no unique or exclusion constraint matching the ON CONFLICT specification. - Încercarea de a actualiza același rând existent de două ori în același lot multi-row
INSERT ... VALUES (...), (...) ON CONFLICT DO UPDATE, ceea ce provoacă eroareaON CONFLICT DO UPDATE command cannot affect row a second time.
Comenzi și funcții înrudite
INSERT INTO ... RETURNING: Inserează unul sau mai multe rânduri și returnează imediat valorile generate (ID-uri, timestamp-uri) fără o interogare suplimentară.UPDATE ... FROM: Actualizează rândurile unui tabel folosind date dintr-un alt tabel sau dintr-o listă VALUES prin clauza FROM.BEGIN, COMMIT, ROLLBACK și SAVEPOINT: Controlează tranzacțiile ACID și punctele intermediare de salvare (SAVEPOINT) pentru recuperarea din erori.