← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

INSERT ... ON DUPLICATE KEY UPDATE

database PHP 5.1+ Intermediate

Also Known As

MySQL upsert ON DUPLICATE KEY UPDATE INSERT OR UPDATE MySQL ODKU

TL;DR

An atomic MySQL upsert — inserts a new row or updates the existing one if a unique key constraint would be violated.

Explanation

ON DUPLICATE KEY UPDATE (ODKU) executes atomically — no race condition between checking existence and inserting. Triggers on any unique key violation (PRIMARY or UNIQUE). VALUES(col) references the value that would have been inserted. REPLACE INTO is similar but deletes then inserts — triggering DELETE + INSERT triggers and resetting AUTO_INCREMENT. Use ODKU for upserts; avoid REPLACE. MySQL 8.0.20+ deprecates VALUES() in favour of aliases: INSERT ... AS new ON DUPLICATE KEY UPDATE col = new.col.

Common Misconception

REPLACE INTO is a safe alternative to ON DUPLICATE KEY UPDATE. REPLACE deletes the old row and inserts a new one — this resets auto-increment IDs, fires DELETE triggers, and breaks foreign keys pointing to the old row.

Why It Matters

The check-then-insert pattern (SELECT to check existence, then INSERT) has a race condition — two concurrent requests can both pass the check and both attempt to insert, causing a duplicate key error. ODKU is atomic.

Common Mistakes

  • Using REPLACE INTO instead of ON DUPLICATE KEY UPDATE — causes unintended cascading deletes via foreign keys.
  • Forgetting that ODKU increments the AUTO_INCREMENT counter even when it updates — gaps in IDs appear.
  • Using VALUES(col) syntax in MySQL 8.0.20+ — deprecated; use the new row alias syntax instead.

Avoid When

  • Do not use REPLACE INTO as a substitute — it deletes and re-inserts, breaking foreign keys.
  • Avoid when the distinction between insert and update needs to trigger different application logic — use a transaction with SELECT FOR UPDATE instead.

When To Use

  • Use for counters, settings, or any row that should be inserted if absent and updated if present.
  • Use when you need an atomic check-then-write with no race condition window.

Code Examples

✗ Vulnerable
// Race condition: two requests both pass the SELECT check
$exists = $pdo->prepare('SELECT id FROM settings WHERE user_id=? AND key=?');
$exists->execute([$uid, $key]);
if (!$exists->fetch()) {
    // Another request may have inserted between here and the line below!
    $pdo->prepare('INSERT INTO settings (user_id, key, value) VALUES (?,?,?)')->execute([$uid,$key,$val]);
}
✓ Fixed
-- Upsert: insert or update view count
INSERT INTO page_views (page_slug, view_count)
VALUES (:slug, 1)
ON DUPLICATE KEY UPDATE view_count = view_count + 1;

-- PHP
$stmt = $pdo->prepare(
    'INSERT INTO user_settings (user_id, `key`, value)
     VALUES (?, ?, ?)
     ON DUPLICATE KEY UPDATE value = VALUES(value)'
);
$stmt->execute([$userId, $key, $value]);

Added 31 Mar 2026
Edited 5 Apr 2026
Views 15
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping W 0 pings T 0 pings F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Perplexity 6 Unknown AI 3 Google 1 Ahrefs 1
crawler 10 pre-tracking 1
DEV INTEL Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Use INSERT ... ON DUPLICATE KEY UPDATE for upserts — eliminates the SELECT + INSERT race condition
📦 Applies To
PHP 5.1+ web cli
🔗 Prerequisites
🔍 Detection Hints
SELECT then INSERT pattern on unique-keyed table without transaction
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update

✓ schema.org compliant