Xóa nhiều bản ghi trùng lặp trong MySQL với JOIN
Sử dụng DELETE với JOIN để xóa các bản ghi trùng lặp dựa trên giá trị cột, giữ lại một bản ghi đại diện.
Ví dụ minh họa
Tạo bảng DemoTable:
mysql> CREATE TABLE DemoTable (
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(100)
);
Query OK, 0 rows affected (0.69 sec)
Chèn dữ liệu (có trùng lặp FirstName):
mysql> INSERT INTO DemoTable(FirstName) VALUES('Chris');
Query OK, 1 row affected (0.19 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Bob');
Query OK, 1 row affected (0.17 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Mike');
Query OK, 1 row affected (0.28 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Adam');
Query OK, 1 row affected (0.33 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Bob');
Query OK, 1 row affected (0.14 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Adam');
Query OK, 1 row affected (0.15 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Bob');
Query OK, 1 row affected (0.12 sec)
mysql> INSERT INTO DemoTable(FirstName) VALUES('Mike');
Query OK, 1 row affected (0.23 sec)
Xem dữ liệu (Bob xuất hiện 3 lần, Adam 2 lần, Mike 2 lần):
mysql> SELECT * FROM DemoTable;
+----+-----------+ | Id | FirstName | +----+-----------+ | 1 | Chris | | 2 | Bob | | 3 | Mike | | 4 | Adam | | 5 | Bob | | 6 | Adam | | 7 | Bob | | 8 | Mike | +----+-----------+ 8 rows in set (0.00 sec)
Xóa các bản ghi trùng FirstName='Bob' (giữ lại 1 bản ghi):
mysql> DELETE DemoTable FROM DemoTable JOIN DemoTable tbl ON DemoTable.FirstName = tbl.FirstName WHERE tbl.FirstName = 'Bob'; Query OK, 3 rows affected (0.15 sec)
Kết quả (Bob chỉ còn 1 bản ghi - Id=2):
mysql> SELECT * FROM DemoTable;
+----+-----------+ | Id | FirstName | +----+-----------+ | 1 | Chris | | 3 | Mike | | 4 | Adam | | 6 | Adam | | 8 | Mike | +----+-----------+ 5 rows in set (0.00 sec)
Giải thích kỹ thuật
DELETE DemoTable FROM DemoTable JOIN DemoTable tbl ...: Cú pháp DELETE với JOIN, chỉ định bảng bị xóa (DemoTable)ON DemoTable.FirstName = tbl.FirstName: Self-join trên cùng bảng, khớp FirstNameWHERE tbl.FirstName = 'Bob': Chỉ xử lý nhóm FirstName='Bob'- Kết quả: Xóa 3 bản ghi Bob (Id=2,5,7) → Giữ lại 1 (thường là Id nhỏ nhất do cách join)
Cách tốt hơn: Xóa trùng lặp, giữ 1 bản ghi (bất kỳ cột nào)
-- Giữ lại bản ghi có Id nhỏ nhất cho mỗi FirstName DELETE t1 FROM DemoTable t1 JOIN DemoTable t2 ON t1.FirstName = t2.FirstName AND t1.Id > t2.Id;
Các phương pháp xóa trùng lặp khác
| Phương pháp | Ưu điểm | Nhược điểm |
|---|---|---|
| DELETE JOIN (trên) | Đơn giản, một câu lệnh | Phức tạp nếu nhiều cột |
| ROW_NUMBER() (MySQL 8.0+) | Rõ ràng, kiểm soát được hàng giữ lại | Cần CTE/subquery |
| Tạo bảng mới + INSERT DISTINCT | Nhanh với bảng lớn | Cần rename, quyền DDL |
| ALTER IGNORE TABLE ADD UNIQUE | Tự động xóa trùng | Deprecated, removed MySQL 8.0 |
Ví dụ ROW_NUMBER() (MySQL 8.0+)
WITH cte AS (
SELECT Id, ROW_NUMBER() OVER (PARTITION BY FirstName ORDER BY Id) AS rn
FROM DemoTable
)
DELETE FROM DemoTable WHERE Id IN (SELECT Id FROM cte WHERE rn > 1);