MySQL UPSERT 教學:INSERT ON DUPLICATE KEY UPDATE 的完整用法與範例

1. 什麼是UPSERT?

概要

「UPSERT」是指在資料庫中結合「INSERT」與「UPDATE」的功能。也就是說,如果資料不存在就插入新的資料;若已存在相同的資料,則更新該資料。透過這個功能,可以在保持資料一致性的同時,更有效率地進行操作。

在 MySQL 中,INSERT ... ON DUPLICATE KEY UPDATE 語法對應此功能。藉由這個語法,即使遇到資料重複的情況,也能避免重複鍵錯誤,並更新既有資料。

使用情境

  • 客戶管理系統:當沒有新客戶資料時新增,有既有客戶資料變更時則更新。
  • 商品庫存管理:新增新商品,同時更新現有商品的庫存數量。

MySQL 中 UPSERT 的優點

  • 避免重複鍵錯誤
  • SQL 查詢語句更簡潔
  • 保持資料的一致性

2. MySQL 中 UPSERT 的基本用法

在 MySQL 中,UPSERT 的操作是透過 INSERT ... ON DUPLICATE KEY UPDATE 語法來實現。當發生重複鍵時,系統不會插入新資料,而是更新現有資料的部分或全部欄位。

基本語法

INSERT INTO 資料表名稱 (欄位1, 欄位2)
VALUES (值1, 值2)
ON DUPLICATE KEY UPDATE
欄位1 = 值1, 欄位2 = 值2;

解說:

  1. 使用 INSERT INTO 插入資料。
  2. 若欲插入的資料已存在於資料表中,則執行 ON DUPLICATE KEY UPDATE 部分,更新既有資料。

範例:

INSERT INTO users (user_id, name)
VALUES (1, '田中太郎')
ON DUPLICATE KEY UPDATE
name = '田中太郎';

以上範例中,若 user_id 為 1 的使用者已存在,則會將其 name 欄位更新為「田中太郎」。若不存在,則插入新資料。

3. UPSERT 的 SQL 語法細節與範例

更新多個欄位的情況

使用 UPSERT 時,也可以只更新部分欄位。在這種情況下,可以在 ON DUPLICATE KEY UPDATE 子句中指定特定欄位。

INSERT INTO products (product_id, name, price)
VALUES (100, '筆記型電腦', 50000)
ON DUPLICATE KEY UPDATE
price = VALUES(price);

此範例中,若 product_id 為 100 的商品已存在,則只更新 price 欄位,其餘欄位(如 name)不會變更。

4. 與其他資料庫的差異

除了 MySQL 之外,其他資料庫也有類似操作。例如 PostgreSQL 與 SQLite 提供 INSERT ... ON CONFLICTMERGE 語法來實現 UPSERT。

PostgreSQL 範例

INSERT INTO users (user_id, name)
VALUES (1, '田中太郎')
ON CONFLICT (user_id) DO UPDATE SET
name = '田中太郎';

在 PostgreSQL 與 SQLite 中,會使用 ON CONFLICT 子句來控制重複鍵錯誤時的行為;而 MySQL 則使用 ON DUPLICATE KEY UPDATE

MySQL 的特殊性

  • MySQL 採用 INSERT ... ON DUPLICATE KEY UPDATE,語法與其他資料庫不同,移轉時需特別注意。

5. 進階 UPSERT 的用法

批次 UPSERT(多筆資料一次處理)

UPSERT 不僅可針對單筆資料,也能一次處理多筆資料,大幅提升資料庫操作效率。

INSERT INTO products (product_id, name, price)
VALUES
(100, '筆記型電腦', 50000),
(101, '智慧型手機', 30000)
ON DUPLICATE KEY UPDATE
price = VALUES(price);

此範例中,一次插入多筆商品資料;若有重複的鍵,則僅更新該商品的價格。

利用儲存程序的 UPSERT

為了讓 UPSERT 更有效率,可以利用儲存程序(Stored Procedure)。這樣能在資料庫內建立可重複使用的程式碼,提升程式的可讀性與維護性。

6. UPSERT 的陷阱與注意事項

交易與死鎖

在使用 UPSERT 時,特別是處理大量資料時,可能會發生死鎖問題。若 MySQL 的交易隔離層級設定為 REPEATABLE READ,更容易引發間隙鎖(Gap Lock)。

避免間隙鎖的方法

  • 將交易隔離層級改為 READ COMMITTED 可降低死鎖發生機率。
  • 必要時可將 UPSERT 操作拆分為多次查詢,以減少風險。

7. 總結

MySQL 的 UPSERT 功能能有效結合資料插入與更新,避免重複鍵錯誤。然而在實作時需注意死鎖風險與交易設定。若能正確使用,將能讓資料庫操作更簡單且高效。