業務常見需求:資料不存在則插入,存在則更新 (或忽略)。MySQL 提供原生語法支援,無需應用層先查後寫,避免競態條件。
-- 唯一鍵衝突時忽略,不插入,不報錯,返回 0 rows affected
INSERT IGNORE INTO users (id, username, email) VALUES (1, 'alice', 'alice@example.com');
-- 若唯一鍵衝突,先 DELETE 舊行,再 INSERT 新行
REPLACE INTO users (id, username, email, updated_at) VALUES (1, 'alice', 'new@example.com', NOW());
DELETE + INSERT,會觸發 ON DELETE 觸發器、外鍵級聯、自增 ID 可能改變。
-- 唯一鍵衝突時執行 UPDATE,不衝突則 INSERT
INSERT INTO users (id, username, email, login_count, last_login)
VALUES (1, 'alice', 'alice@example.com', 1, NOW())
ON DUPLICATE KEY UPDATE
login_count = login_count + 1,
last_login = NOW(),
email = VALUES(email); -- VALUES(col) 取得插入值
VALUES(col) 可引用插入值,避免重複寫字面量。LAST_INSERT_ID() 獲取自增 ID (衝突時返回現有 ID)。| 特性 | INSERT IGNORE | REPLACE INTO | ON DUPLICATE KEY UPDATE |
|---|---|---|---|
| 衝突行為 | 忽略 | 刪除舊+插入新 | 更新指定欄位 |
| 觸發器 | 不觸發 | 觸發 DELETE/INSERT | 觸發 UPDATE |
| 自增 ID | 不變 | 可能改變 | 不變 |
| 外鍵級聯 | 不觸發 | 觸發 ON DELETE | 不觸發 |
| 適用場景 | 純去重、不更新 | 全行替換、接受副作用 | 精確 Upsert、最常用 |
| 效能 | 最快 | 較慢 (雙操作) | 中等 |
表必須有 PRIMARY KEY 或 UNIQUE KEY,否則無法判定「重複」,語法會報錯或全表掃描。
-- 批次 Upsert
INSERT INTO users (id, name, score) VALUES
(1, 'a', 10), (2, 'b', 20), (3, 'c', 30)
ON DUPLICATE KEY UPDATE score = VALUES(score);
-- 條件更新:僅當新值更大時更新
INSERT INTO stats (date, metric, value) VALUES ('2024-01-15', 'pv', 1000)
ON DUPLICATE KEY UPDATE value = GREATEST(value, VALUES(value));
-- 使用 LAST_INSERT_ID(expr) 使 ON DUPLICATE 也能返回自增 ID
INSERT INTO t (id, val) VALUES (NULL, 100)
ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), val = VALUES(val);
SELECT LAST_INSERT_ID(); -- 返回 INSERT 或 UPDATE 的 id
SHOW GLOBAL STATUS LIKE 'Innodb_rows_inserted/updated/deleted'; 觀察寫入模式。