UPSERT tarkoittaa "lisää, tai päivitä jos se on jo olemassa". PostgreSQL toteuttaa tämän komennolla INSERT ... ON CONFLICT — lisää rivi, mutta jos se rikkoisi yksilöllisyysrajoitetta (unique constraint), päivitä sen sijaan olemassa oleva rivi (tai älä tee mitään). Se hoitaa yleisen "lisää-tai-päivitä"-tarpeen atomisesti.
Ongelma, jonka UPSERT ratkaisee
You want to insert a row, but it might already exist (by a unique key):
❌ a plain INSERT fails with a unique-violation error if it exists
❌ check-then-insert (SELECT, then INSERT or UPDATE) has a RACE CONDITION
(another transaction could insert between your check and insert)
→ ON CONFLICT does it atomically in ONE statement (no race condition)
ON CONFLICT DO UPDATE — lisää tai päivitä
INSERT INTO users (email, name, login_count)
VALUES ('[email protected]', 'Ann', 1)
ON CONFLICT (email) -- if a row with this email already exists...
DO UPDATE SET -- ...UPDATE it instead
name = EXCLUDED.name, -- EXCLUDED = the values you tried to insert
login_count = users.login_count + 1; -- can reference the existing row too
Jos lisääminen aiheuttaisi konfliktin email-yksilöllisyysrajoitteen kanssa, Postgres päivittää sen sijaan olemassa olevan rivin. EXCLUDED viittaa arvoihin, joita yritit lisätä, ja voit viitata myös olemassa olevaan riviin (users.login_count) — mahdollistaen logiikan kuten laskurin kasvattamisen.
ON CONFLICT DO NOTHING — lisää tai ohita
INSERT INTO users (email, name) VALUES ('[email protected]', 'Ann')
ON CONFLICT (email) DO NOTHING; -- if it exists, just skip (no error)
DO NOTHING ohittaa hiljaisesti konfliktoivat rivit — hyödyllinen "lisää jos ei ole olemassa" -tarpeisiin ilman virheitä.
Yleinen todellinen käyttö: laskurit ja idempotentit lisäykset
-- track page views — insert on first view, increment on subsequent
INSERT INTO page_views (page_id, views) VALUES (5, 1)
ON CONFLICT (page_id) DO UPDATE SET views = page_views.views + 1;
Miksi se on tärkeää
UPSERT (INSERT ... ON CONFLICT) on tärkeä, käytännössä arvokas PostgreSQL-ominaisuus, joka ratkaisee hyvin yleisen "lisää-tai-päivitä"-tarpeen siististi ja turvallisesti, joten sen ymmärtäminen on hyödyllistä tosielämän dataoperaatioissa.
Tarve — sellaisen rivin lisääminen, joka saattaa jo olla olemassa (yksilöllisen avaimen perusteella) — esiintyy jatkuvasti (tietueiden upsert, laskurit, idempotentit lisäykset, datan synkronointi), ja naiivit vaihtoehdot ovat ongelmallisia: tavallinen INSERT epäonnistuu virheellä, jos rivi on olemassa, ja tarkista-sitten-lisää-lähestymistavalla (SELECT sitten INSERT-tai-UPDATE) on kilpailutilanne (race condition) (toinen samanaikainen transaktio voisi lisätä rivin tarkistuksesi ja lisäyksesi välissä, aiheuttaen virheitä tai menetettyjä päivityksiä). ON CONFLICT ratkaisee tämän atomisesti yhdellä lauseella — poistaen kilpailutilanteen, mikä on sen keskeinen oikeellisuushyöty samanaikaisissa sovelluksissa.
Molempien muotojen ymmärtäminen — DO UPDATE (lisää tai päivitä, jossa EXCLUDED viittaa yritettyihin arvoihin ja kyky viitata olemassa olevaan riviin logiikkaa varten kuten laskurien kasvattaminen) ja DO NOTHING (lisää tai ohita hiljaisesti) — kattaa yleiset kuviot (tietueiden upsert, idempotentit lisäykset, laskurien/aggregaattien ylläpito).
Koska lisää-tai-päivitä on yleinen operaatio ja sen tekeminen oikein ja turvallisesti (atomisesti, ilman kilpailutilanteita) on tärkeää datan eheydelle samanaikaisissa sovelluksissa, UPSERTin ymmärtäminen — mitä se ratkaisee, atomisuus-/kilpailutilannehyöty sekä DO UPDATE/DO NOTHING -muodot — on arvokasta, usein sovellettavaa PostgreSQL-tietoa, joka tarjoaa siistin, turvallisen ratkaisun yleiseen tarpeeseen, joka muuten on virhealtis, tehden siitä käytännöllisen, tärkeän ominaisuuden todellisille sovelluksille.
