--- title: Use UPSERT for Insert-or-Update Operations impact: MEDIUM impactDescription: Atomic operation, eliminates race conditions tags: upsert, on-conflict, insert, update --- ## Use UPSERT for Insert-or-Update Operations Using separate SELECT-then-INSERT/UPDATE creates race conditions. Use INSERT ... ON CONFLICT for atomic upserts. **Incorrect (check-then-insert race condition):** ```sql -- Race condition: two requests check simultaneously select * from settings where user_id = 123 and key = 'theme'; -- Both find nothing -- Both try to insert insert into settings (user_id, key, value) values (123, 'theme', 'dark'); -- One succeeds, one fails with duplicate key error! ``` **Correct (atomic UPSERT):** ```sql -- Single atomic operation insert into settings (user_id, key, value) values (123, 'theme', 'dark') on conflict (user_id, key) do update set value = excluded.value, updated_at = now(); -- Returns the inserted/updated row insert into settings (user_id, key, value) values (123, 'theme', 'dark') on conflict (user_id, key) do update set value = excluded.value returning *; ``` Insert-or-ignore pattern: ```sql -- Insert only if not exists (no update) insert into page_views (page_id, user_id) values (1, 123) on conflict (page_id, user_id) do nothing; ``` Reference: [INSERT ON CONFLICT](https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT)