Usuwanie duplikatów w tabelach MySQL jest częstym problemem, który ogólnie wynika z brakującego ograniczenia, aby uniknąć tych duplikatów przed ręką. Ale ten powszechny problem zwykle wiąże się z konkretnymi potrzebami ... które wymagają określonego podejścia. Podejście powinno być różne w zależności na przykład od wielkości danych, powielonego wpisu, który powinien być zachowany (zazwyczaj pierwszy lub ostatni), od tego, czy należy zachować indeksy, czy też chcemy wykonać dodatkowe działanie na zduplikowanych danych.
Istnieją również pewne specyficzne cechy samego MySQL, takie jak brak możliwości odwołania się do tej samej tabeli na podstawie FROM podczas wykonywania aktualizacji tabeli (spowoduje to błąd MySQL # 1093). Ograniczenie to można pokonać, używając wewnętrznego zapytania z tabelą tymczasową (jak sugerowano w niektórych podejściach powyżej). Ale to wewnętrzne zapytanie nie będzie szczególnie skuteczne w przypadku źródeł dużych zbiorów danych.
Istnieje jednak lepsze podejście do usuwania duplikatów, które jest zarówno wydajne, jak i niezawodne i można je łatwo dostosować do różnych potrzeb.
Ogólnym pomysłem jest utworzenie nowej tabeli tymczasowej, zwykle dodając unikalne ograniczenie, aby uniknąć dalszych duplikatów, i WSTAWIĆ dane z poprzedniej tabeli do nowej, jednocześnie dbając o duplikaty. Podejście to opiera się na prostych zapytaniach MySQL INSERT, tworzy nowe ograniczenie, aby uniknąć dalszych duplikatów, i pomija potrzebę stosowania wewnętrznego zapytania do wyszukiwania duplikatów i tabeli tymczasowej, którą należy przechowywać w pamięci (dopasowując również źródła dużych danych).
Tak można to osiągnąć. Biorąc pod uwagę, że mamy pracownika tabeli , z następującymi kolumnami:
employee (id, first_name, last_name, start_date, ssn)
Aby usunąć wiersze ze zduplikowaną kolumną ssn i zachować tylko pierwszy znaleziony wpis, można wykonać następujący proces:
-- 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;
Wyjaśnienie techniczne
- Wiersz nr 1 tworzy nową tabelę tmp_eployee o dokładnie takiej samej strukturze jak tabela pracowników
- Wiersz # 2 dodaje ograniczenie UNIKALNE do nowej tabeli tmp_eployee, aby uniknąć dalszych duplikatów
- Wiersz # 3 skanuje oryginalną tabelę pracowników według identyfikatora, wstawiając nowe wpisy pracowników do nowej tabeli tmp_eployee , ignorując powielone wpisy
- Wiersz nr 4 zmienia nazwy tabel, dzięki czemu nowa tabela pracowników przechowuje wszystkie wpisy bez duplikatów, a kopia zapasowa wcześniejszych danych jest przechowywana w tabeli backup_employee
⇒ Korzystając z tego podejścia, 1,6 mln rejestrów zostało przekonwertowanych na 6k w mniej niż 200s.
Chetan , postępując zgodnie z tym procesem, możesz szybko i łatwo usunąć wszystkie swoje duplikaty i utworzyć UNIKALNE ograniczenie, uruchamiając:
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;
Oczywiście proces ten można dodatkowo zmodyfikować, aby dostosować go do różnych potrzeb podczas usuwania duplikatów. Oto kilka przykładów.
✔ Wariant dla zachowania ostatniego wpisu zamiast pierwszego
Czasami musimy zachować ostatni zduplikowany wpis zamiast pierwszego.
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;
- W wierszu nr 3 klauzula ORDER BY id DESC sprawia, że ostatnie identyfikatory mają pierwszeństwo przed resztą
✔ Wariacja wykonywania niektórych zadań na duplikatach, na przykład utrzymywanie liczby znalezionych duplikatów
Czasami musimy wykonać dalsze przetwarzanie znalezionych duplikatów (na przykład rejestrowanie liczby duplikatów).
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;
- W linii nr 3 tworzona jest nowa kolumna n_duplikaty
- W linii nr 4 zapytanie INSERT INTO ... ON DUPLICATE KEY UPDATE służy do wykonania dodatkowej aktualizacji po znalezieniu duplikatu (w tym przypadku zwiększenie licznika) Zapytanie INSERT INTO ... ON DUPLICATE KEY UPDATE może być służy do wykonywania różnych typów aktualizacji dla znalezionych duplikatów.
✔ Wariant regeneracji automatycznego pola przyrostowego id
Czasami używamy pola auto-inkrementalnego i, aby utrzymać możliwie jak najmniejszy indeks, możemy skorzystać z usunięcia duplikatów, aby zregenerować pole auto-inkrementalne w nowej tabeli tymczasowej.
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;
- W wierszu nr 3 zamiast wybierać wszystkie pola w tabeli pole ID jest pomijane, dzięki czemu silnik DB automatycznie generuje nowe pole
✔ Dalsze warianty
Wiele dalszych modyfikacji jest również wykonalnych w zależności od pożądanego zachowania. Jako przykład, poniższe zapytania wykorzystają drugą tabelę tymczasową, aby oprócz 1) zachować ostatni wpis zamiast pierwszego; oraz 2) zwiększyć licznik znalezionych duplikatów; także 3) zregeneruj identyfikator pola auto-inkrementalnego, zachowując kolejność wpisów, tak jak w poprzednich danych.
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;