Upsert trong MySQL: INSERT ... ON DUPLICATE KEY UPDATE
Cơ chế INSERT ... ON DUPLICATE KEY UPDATE (thường gọi là "upsert"): Thử INSERT, nếu vi phạm PRIMARY KEY hoặc UNIQUE KEY → tự động chuyển sang UPDATE hàng hiện có.
Ví dụ minh họa
Tạo bảng DemoTable733 với UNIQUE KEY trên StudentName:
mysql> CREATE TABLE DemoTable733 (
StudentId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
StudentName VARCHAR(100),
StudentMarks INT,
UNIQUE KEY Un_Name (StudentName)
);
Query OK, 0 rows affected (0.60 sec)
Thực hiện upsert:
mysql> INSERT INTO DemoTable733(StudentName,StudentMarks) VALUES('John',45) ON DUPLICATE KEY UPDATE StudentMarks=86;
Query OK, 1 row affected (0.19 sec)
mysql> INSERT INTO DemoTable733(StudentName,StudentMarks) VALUES('Adam',65) ON DUPLICATE KEY UPDATE StudentMarks=86;
Query OK, 1 row affected (0.15 sec)
mysql> INSERT INTO DemoTable733(StudentName,StudentMarks) VALUES('Carol',75) ON DUPLICATE KEY UPDATE StudentMarks=86;
Query OK, 1 row affected (0.08 sec)
mysql> INSERT INTO DemoTable733(StudentName,StudentMarks) VALUES('John',45) ON DUPLICATE KEY UPDATE StudentMarks=86;
Query OK, 2 rows affected (0.12 sec)
Kết quả (John được UPDATE Marks từ 45 → 86, không tạo hàng mới):
mysql> SELECT * FROM DemoTable733;
+-----------+-------------+--------------+ | StudentId | StudentName | StudentMarks | +-----------+-------------+--------------+ | 1 | John | 86 | | 2 | Adam | 65 | | 3 | Carol | 75 | +-----------+-------------+--------------+ 3 rows in set (0.00 sec)
Giải thích rows affected
1 row affected: INSERT mới thành công2 rows affected: UPDATE hàng hiện có (MySQL đếm: 1 delete + 1 insert logic)0 rows affected: UPDATE nhưng giá trị không thay đổi
Sử dụng VALUES() để tham chiếu giá trị INSERT
INSERT INTO DemoTable733(StudentName,StudentMarks) VALUES('John',90)
ON DUPLICATE KEY UPDATE StudentMarks = VALUES(StudentMarks) + 10;
Nếu John tồn tại: Marks = 90 + 10 = 100. Nếu mới: Marks = 90.
Nhiều cột UNIQUE / Composite Key
CREATE TABLE scores (
student_id INT,
subject_id INT,
score INT,
UNIQUE KEY uq_student_subject (student_id, subject_id)
);
INSERT INTO scores VALUES(1, 101, 85)
ON DUPLICATE KEY UPDATE score = VALUES(score);
Lưu ý quan trọng
- Bắt buộc có PRIMARY KEY hoặc UNIQUE INDEX mới kích hoạt ON DUPLICATE KEY
- Không hoạt động với FOREIGN KEY violation
- Trigger:
BEFORE INSERTvàBEFORE UPDATEđều chạy (INSERT trước, nếu trùng thì UPDATE) - Auto-increment: Giá trị ID vẫn tăng kể cả khi UPDATE (có thể tạo lỗ hổng ID)
- Replication: Statement-based replication có thể gây inconsistency → dùng ROW-based
MySQL 8.0.20+: Alias cho VALUES()
INSERT INTO t(a,b) VALUES(1,2) AS new ON DUPLICATE KEY UPDATE b = new.b + 1;