萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL ON DUPLICATE KEY UPDATE 實現 Upsert(存在更新不存在插入)

語法核心

INSERT INTO `table` (pk_col, col1, col2) 
VALUES (1, 'a', 'b')
ON DUPLICATE KEY UPDATE 
    col1 = VALUES(col1), 
    col2 = VALUES(col2);

觸發條件

  • 表有 PRIMARY KEY 或 UNIQUE INDEX
  • 插入數據導致鍵衝突
  • 衝突時執行 UPDATE 部分,否常插入新行

影響行數語義

情況ROW_COUNT()LAST_INSERT_ID()
新插入 1 行1新自增 ID
更新現有 1 行2舊自增 ID(除非用 LAST_INSERT_ID(id) 技巧)
多行插入,部分更新插入行數 + 2×更新行數第一行插入的自增 ID

VALUES() 函數:引用插入值

INSERT INTO `stats` (date, type, count) 
VALUES 
    ('2024-01-15', 'pv', 1),
    ('2024-01-15', 'uv', 1)
ON DUPLICATE KEY UPDATE 
    `count` = `count` + VALUES(`count`);
  • VALUES(col) 返回 VALUES 子句中該列的值
  • 僅在 ON DUPLICATE KEY UPDATE 子句中有效,其他上下文返回 NULL
  • 極大簡化批量 upsert 邏輯

進階用法

條件更新

INSERT INTO `inventory` (sku, qty, version) 
VALUES ('SKU001', 10, 1)
ON DUPLICATE KEY UPDATE 
    qty = IF(VALUES(qty) > qty, VALUES(qty), qty),  -- 只增不減
    version = version + 1;

忽略特定衝突

-- 僅在新插入時設置 created_at,更新時保持原值
INSERT INTO `users` (email, name, created_at) 
VALUES ('a@b.com', 'Alice', NOW())
ON DUPLICATE KEY UPDATE 
    name = VALUES(name),
    updated_at = NOW();  -- created_at 不變

返回插入/更新標識

INSERT INTO `logs` (msg) VALUES ('test')
ON DUPLICATE KEY UPDATE 
    id = LAST_INSERT_ID(id),  -- 關鍵技巧
    cnt = cnt + 1;
SELECT LAST_INSERT_ID();  -- 統一返回該行 ID

與 REPLACE INTO 對比

特性INSERT ... ON DUPLICATE KEY UPDATEREPLACE INTO
機制衝突時 UPDATE衝突時 DELETE + INSERT
自增 ID保持不變(更新)生成新 ID
外鍵關聯安全級聯刪除風險
觸發器觸發 UPDATE觸發 DELETE + INSERT
性能優(就地更新)差(雙重操作)

注意事項

  • DELAYED 忽略:INSERT DELAYED ... ON DUPLICATE KEY 中 DELAYED 無效
  • 多唯一鍵風險:若表有多個 UNIQUE KEY,衝突行不確定(任一匹配即更新),盡量避免
  • 分區表限制:分區鍵必須包含在唯一鍵中,否則報錯
  • 外鍵檢查:更新時仍檢查外鍵約束
  • 權限:需目標表 INSERT + UPDATE 權限

實戰模板:計數器原子增減

CREATE TABLE `counter` (
    `name` VARCHAR(64) PRIMARY KEY,
    `value` BIGINT NOT NULL DEFAULT 0,
    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 原子增量
INSERT INTO `counter` (`name`, `value`) VALUES ('page_view', 1)
ON DUPLICATE KEY UPDATE 
    `value` = `value` + VALUES(`value`),
    `updated_at` = NOW();

總結

ON DUPLICATE KEY UPDATE 是 MySQL 實現 Upsert 的標準、高效、原子方式。善用 VALUES() 引用插入值、LAST_INSERT_ID(id) 統一返回 ID,可構建簡潔健壯的寫入邏輯。避免多唯一鍵表使用,配合合理索引設計,是高併發寫入場景的利器。

copyright © 萬盛學電腦網 all rights reserved