Xóa các bản sao trên các bảng MySQL là một vấn đề phổ biến, đó thực sự là kết quả của một ràng buộc bị thiếu để tránh các bản sao đó trước khi xử lý. Nhưng vấn đề phổ biến này thường đi kèm với các nhu cầu cụ thể ... đòi hỏi phải có cách tiếp cận cụ thể. Cách tiếp cận nên khác nhau tùy thuộc vào, ví dụ, kích thước của dữ liệu, mục nhập trùng lặp nên được giữ (nói chung là đầu tiên hoặc cuối cùng), liệu có các chỉ mục được giữ hay không, hoặc chúng tôi muốn thực hiện bất kỳ bổ sung nào hành động trên dữ liệu trùng lặp.
Ngoài ra còn có một số đặc điểm cụ thể trên MySQL, chẳng hạn như không thể tham chiếu cùng một bảng với nguyên nhân TỪ khi thực hiện bảng CẬP NHẬT (nó sẽ gây ra lỗi MySQL # 1093). Hạn chế này có thể được khắc phục bằng cách sử dụng truy vấn bên trong với bảng tạm thời (như được đề xuất trên một số phương pháp ở trên). Nhưng truy vấn bên trong này sẽ không thực hiện đặc biệt tốt khi xử lý các nguồn dữ liệu lớn.
Tuy nhiên, một cách tiếp cận tốt hơn tồn tại để loại bỏ các bản sao, điều đó vừa hiệu quả vừa đáng tin cậy và có thể dễ dàng thích nghi với các nhu cầu khác nhau.
Ý tưởng chung là tạo một bảng tạm thời mới, thường thêm một ràng buộc duy nhất để tránh trùng lặp thêm và để XÁC NHẬN dữ liệu từ bảng cũ của bạn sang bảng mới, trong khi chăm sóc các bản sao. Cách tiếp cận này dựa trên các truy vấn MySQL INSERT đơn giản, tạo ra một ràng buộc mới để tránh trùng lặp thêm và bỏ qua nhu cầu sử dụng truy vấn bên trong để tìm kiếm các bản sao và bảng tạm thời nên được giữ trong bộ nhớ (do đó cũng phù hợp với các nguồn dữ liệu lớn).
Đây là cách nó có thể đạt được. Cho rằng chúng tôi có một nhân viên bảng , với các cột sau:
employee (id, first_name, last_name, start_date, ssn)
Để xóa các hàng có cột ssn trùng lặp và chỉ giữ lại mục nhập đầu tiên được tìm thấy, có thể thực hiện quy trình sau:
-- create a new tmp_eployee table
CREATE TABLE tmp_employee LIKE employee;
-- add a unique constraint
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
-- scan over the employee table to insert employee entries
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id;
-- rename tables
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Giải thích kỹ thuật
- Dòng # 1 tạo bảng tmp_eployee mới với cấu trúc chính xác giống như bảng nhân viên
- Dòng # 2 thêm một ràng buộc ĐỘC ĐÁO vào bảng tmp_eployee mới để tránh bất kỳ sự trùng lặp nào nữa
- Dòng số 3 quét qua bảng nhân viên ban đầu theo id, chèn các mục nhập nhân viên mới vào bảng tmp_eployee mới , trong khi bỏ qua các mục trùng lặp
- Dòng số 4 đổi tên các bảng, để bảng nhân viên mới giữ tất cả các mục nhập mà không trùng lặp và một bản sao lưu dữ liệu cũ được giữ trên bảng backup_employee
⇒ Sử dụng phương pháp này, 1,6 triệu đăng ký được chuyển đổi thành 6k trong vòng chưa đầy 200s.
Chetan , theo quy trình này, bạn có thể nhanh chóng và dễ dàng loại bỏ tất cả các bản sao của mình và tạo ra một ràng buộc ĐỘC ĐÁO bằng cách chạy:
CREATE TABLE tmp_jobs LIKE jobs;
ALTER TABLE tmp_jobs ADD UNIQUE(site_id, title, company);
INSERT IGNORE INTO tmp_jobs SELECT * FROM jobs ORDER BY id;
RENAME TABLE jobs TO backup_jobs, tmp_jobs TO jobs;
Tất nhiên, quá trình này có thể được sửa đổi thêm để điều chỉnh nó cho các nhu cầu khác nhau khi xóa các bản sao. Một số ví dụ sau đây.
✔ Biến thể để giữ mục cuối cùng thay vì mục đầu tiên
Đôi khi chúng ta cần giữ mục nhập trùng lặp cuối cùng thay vì mục đầu tiên.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id DESC;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- Trên dòng số 3, mệnh đề ORDER BY id DESC làm cho ID cuối cùng được ưu tiên hơn phần còn lại
✔ Biến thể để thực hiện một số tác vụ trên các bản sao, ví dụ: giữ số lượng trên các bản sao được tìm thấy
Đôi khi chúng ta cần thực hiện một số xử lý tiếp theo đối với các mục trùng lặp được tìm thấy (chẳng hạn như giữ một số lượng trùng lặp).
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- Trên dòng số 3, một cột mới n_d repeatates được tạo
- Trên dòng số 4, truy vấn INSERT INTO ... ON DUPLICATE KEY UPDATE được sử dụng để thực hiện cập nhật bổ sung khi tìm thấy bản sao (trong trường hợp này, tăng bộ đếm) Có thể truy vấn INSERT INTO ... ON DUPLICATE KEY UPDATE được sử dụng để thực hiện các loại cập nhật khác nhau cho các bản sao được tìm thấy.
✔ Biến thể để tạo lại id trường tăng tự động
Đôi khi chúng tôi sử dụng trường tăng tự động và để giữ cho chỉ số càng nhỏ gọn càng tốt, chúng tôi có thể tận dụng việc xóa các bản sao để tạo lại trường tăng tự động trong bảng tạm thời mới.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT (first_name, last_name, start_date, ssn) FROM employee ORDER BY id;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- Trên dòng số 3, thay vì chọn tất cả các trường trên bảng, trường id được bỏ qua để công cụ DB tự động tạo một trường mới
✔ Các biến thể khác
Nhiều sửa đổi thêm cũng có thể thực hiện được tùy thuộc vào hành vi mong muốn. Ví dụ: các truy vấn sau sẽ sử dụng bảng tạm thời thứ hai để, ngoài 1) giữ mục nhập cuối cùng thay vì mục đầu tiên; và 2) tăng bộ đếm trên các bản sao được tìm thấy; cũng 3) tạo lại id trường tăng tự động trong khi vẫn giữ thứ tự nhập như trên dữ liệu cũ.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id DESC ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
CREATE TABLE tmp_employee2 LIKE tmp_employee;
INSERT INTO tmp_employee2 SELECT (first_name, last_name, start_date, ssn) FROM tmp_employee ORDER BY id;
DROP TABLE tmp_employee;
RENAME TABLE employee TO backup_employee, tmp_employee2 TO employee;