Usuń zduplikowane wiersze w MySQL

375

Mam tabelę z następującymi polami:

id (Unique)
url (Unique)
title
company
site_id

Teraz muszę usunąć wiersze mające takie same title, company and site_id. Jednym ze sposobów będzie użycie następującego kodu SQL wraz ze skryptem ( PHP):

SELECT title, site_id, location, id, count( * ) 
FROM jobs
GROUP BY site_id, company, title, location
HAVING count( * ) >1

Po uruchomieniu tego zapytania mogę usunąć duplikaty za pomocą skryptu po stronie serwera.

Ale chcę wiedzieć, czy można to zrobić tylko za pomocą zapytania SQL.

Chetan
źródło
1
Szybkie pytanie: czy zawsze chcesz, aby duplikat (tytuł, firma, identyfikator strony) nie istniał? Jeśli tak, ustawiłbym ograniczenie w bazie danych, aby wymusić unikalność tytułu, firmy i site_id. Co oznaczałoby, że nie potrzebujesz procesu czyszczenia. I zajmuje tylko jedną linię SQL.
J. Polfer
1
Proszę odnieść się do tego linku Stackoverflow. To działało dla mnie jako urok.
Mogę polecić to rozwiązanie (opublikowane w innym wątku): stackoverflow.com/a/4685232/195835
Simon East
Możesz również sprawdzić tę odpowiedź
Jose Rui Santos

Odpowiedzi:

607

Bardzo łatwym sposobem na to jest dodanie UNIQUEindeksu do 3 kolumn. Podczas pisania ALTERinstrukcji dołącz IGNOREsłowo kluczowe. Tak jak:

ALTER IGNORE TABLE jobs
ADD UNIQUE INDEX idx_name (site_id, title, company);

Spowoduje to usunięcie wszystkich zduplikowanych wierszy. Dodatkową korzyścią jest, INSERTsże przyszłe duplikaty będą błędne. Jak zawsze, możesz wykonać kopię zapasową przed uruchomieniem czegoś takiego ...

Chris Henry
źródło
8
Ciekawe , ale założenia, które zawiera klauzula IGNORE do usuwania tych duplikatów, mogą budzić obawy, które mogą nie spełniać potrzeb. Nieprawidłowe wartości obcięte do najbliższego akceptowalnego dopasowania brzmią dobrze?
OMG Ponies
75
Dla przypomnienia, jeśli korzystasz z InnoDB, możesz mieć z tym problem, istnieje znany błąd dotyczący używania ALTER IGNORE TABLE z bazami danych InnoDB.
DarkMantis,
27
Wspomniany wyżej błąd @DarkMantis, o którym mowa, i jego rozwiązanie .
Jordan Arseno
42
W przypadku tabel InnoDB wykonaj najpierw następujące zapytanie:set session old_alter_table=1;
shock_one
51
To nie jest już obsługiwane w 5.7.4, dev.mysql.com/doc/refman/5.7/en/alter-table.html
Ray Baxter
180

Jeśli nie chcesz zmieniać właściwości kolumny, możesz użyć zapytania poniżej.

Ponieważ masz kolumnę, która ma unikalne identyfikatory (np. auto_incrementKolumny), możesz jej użyć do usunięcia duplikatów:

DELETE `a`
FROM
    `jobs` AS `a`,
    `jobs` AS `b`
WHERE
    -- IMPORTANT: Ensures one version remains
    -- Change "ID" to your unique column's name
    `a`.`ID` < `b`.`ID`

    -- Any duplicates you want to check for
    AND (`a`.`title` = `b`.`title` OR `a`.`title` IS NULL AND `b`.`title` IS NULL)
    AND (`a`.`company` = `b`.`company` OR `a`.`company` IS NULL AND `b`.`company` IS NULL)
    AND (`a`.`site_id` = `b`.`site_id` OR `a`.`site_id` IS NULL AND `b`.`site_id` IS NULL);

W MySQL możesz to jeszcze bardziej uprościć dzięki operatorowi równości NULL-bezpieczne (inaczej „operatorowi statku kosmicznego” ):

DELETE `a`
FROM
    `jobs` AS `a`,
    `jobs` AS `b`
WHERE
    -- IMPORTANT: Ensures one version remains
    -- Change "ID" to your unique column's name
    `a`.`ID` < `b`.`ID`

    -- Any duplicates you want to check for
    AND `a`.`title` <=> `b`.`title`
    AND `a`.`company` <=> `b`.`company`
    AND `a`.`site_id` <=> `b`.`site_id`;
rehriff
źródło
3
to rozwiązanie nie działa poprawnie, próbowałem zrobić kilka zduplikowanych rekordów i robi coś takiego (dotyczy 20 wierszy), ale jeśli uruchomisz go ponownie, pokaże (dotyczy 4 wierszy) i tak dalej, aż dojdziesz (dotyczy 0 wierszy) co jest dość podejrzane i oto, co działa najlepiej dla mnie, jest prawie takie samo, ale działa w jednym przebiegu, edytowałem rozwiązanie
Nassim
1
@Nassim: Musisz robić coś innego niż ta odpowiedź, ponieważ działa ona idealnie dla mnie (w MySQL).
Lawrence Dol
3
Dla każdego, kto był zdezorientowany, jak ja, warunki porównania NULL są potrzebne, ponieważ NULL nie jest równy NULL w MySQL. Jeśli odpowiednie kolumny nie mają wartości NULL, możesz pominąć te warunki.
Ian
3
Tak, zaakceptowana odpowiedź nie jest już ważna, ponieważ MYSQL 5.7, więc to naprawdę powinna być zaakceptowana odpowiedź, ponieważ jest uniwersalna i nie wymaga również tymczasowego tworzenia tabeli.
that-ben
1
BARDZO WOLNY, jeśli istnieje WIELE kopii danego rekordu (np. 100 należy zmniejszyć do 1) i wiele rekordów z tym warunkiem. Zamiast tego polecam stackoverflow.com/a/4685232/199364 . IMHO, ZAWSZE stosuj podejście powiązane; jest to z natury szybsza technika.
ToolmakerSteve
78

MySQL ma ograniczenia dotyczące odwoływania się do tabeli, z której usuwasz. Możesz obejść ten problem przy tymczasowym stole, takim jak:

create temporary table tmpTable (id int);

insert  into tmpTable
        (id)
select  id
from    YourTable yt
where   exists
        (
        select  *
        from    YourTabe yt2
        where   yt2.title = yt.title
                and yt2.company = yt.company
                and yt2.site_id = yt.site_id
                and yt2.id > yt.id
        );

delete  
from    YourTable
where   ID in (select id from tmpTable);

Z sugestii Kostanosa w komentarzach:
Jedyne wolne zapytanie powyżej to DELETE, w przypadkach, gdy masz bardzo dużą bazę danych. To zapytanie może być szybsze:

DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
Andomar
źródło
3
@ andomar, działa to dobrze, z wyjątkiem sytuacji, gdy jedno z pól w klauzuli where zawiera wartości null. Przykład: sqlfiddle.com/#!2/983f3/1
program kodujący
1
Czy Insert SQL jest drogi? Zastanawiam się, ponieważ upłynął limit czasu w mojej bazie danych MySQL.
Cassio
4
Jedynym powolnym zapytaniem jest DELETE, w przypadku dużej bazy danych. To zapytanie może być szybsze:DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
Kostanos,
@Kostanos Nie tylko DELETE, ale także INSERTdo tymczasowego stołu, zajęło mi to dużo czasu. Tak więc indeks tabeli tmp może bardzo pomóc create index tmpTable_id_index on tmpTable (id), przynajmniej dla mnie.
Jiezhi.G
1
Jeśli twoje stoły są duże, warto dziko dodać indeks z: -create temporary table tmpTable (id int, PRIMARY KEY (id));
Dallas Clarke
44

Jeśli IGNOREinstrukcja nie będzie działać tak jak w moim przypadku, możesz użyć poniższej instrukcji:

CREATE TABLE your_table_deduped LIKE your_table;


INSERT your_table_deduped
SELECT *
FROM your_table
GROUP BY index1_id,
         index2_id;

RENAME TABLE your_table TO your_table_with_dupes;

RENAME TABLE your_table_deduped TO your_table;

#OPTIONAL
ALTER TABLE `your_table` ADD UNIQUE `unique_index` (`index1_id`, `index2_id`);

#OPTIONAL
DROP TABLE your_table_with_dupes;
Kamil
źródło
1
działa świetnie, jeśli masz ustawienie innoDB z ograniczeniem klucza obcego.
magdmartin,
@magdmartin, ale czy obce ograniczenia nie zapobiegną usunięciu tabeli?
Basilevs,
1
Oświadczenie IGNORE nie zadziałało dla mnie i świetnie działało na deduplikacji 5 milionów rekordów. Twoje zdrowie.
Mauvis Ledford,
32

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;
César Revert-Gomar
źródło
27

Istnieje inne rozwiązanie:

DELETE t1 FROM my_table t1, my_table t2 WHERE t1.id < t2.id AND t1.my_field = t2.my_field AND t1.my_field_2 = t2.my_field_2 AND ...
Mostafa -T
źródło
4
Czym różni się to od odpowiedzi @ rehriff, którą przesłał 6 miesięcy wcześniej?
Lawrence Dol
@LawrenceDol Myślę, że jest to trochę bardziej czytelne, a także myślę, że jego odpowiedź nie była taka sama w chwili, gdy odpowiedziałem i myślę, że jego odpowiedź została zredagowana.
Mostafa
1
hmm Trwa to dla mnie zbyt długo, a liczba rekordów nie była duża!
SuB
8

jeśli masz duży stół z ogromną liczbą rekordów, powyższe rozwiązania nie będą działać lub zajmują zbyt dużo czasu. Mamy więc inne rozwiązanie

-- Create temporary table

CREATE TABLE temp_table LIKE table1;

-- Add constraint
ALTER TABLE temp_table ADD UNIQUE(title, company,site_id);

-- Copy data
INSERT IGNORE INTO temp_table SELECT * FROM table1;

-- Rename and drop
RENAME TABLE table1 TO old_table1, temp_table TO table1;
DROP TABLE old_table1;
faisalbhagat
źródło
6

Mam ten fragment zapytania dla SQLServer, ale myślę, że można go używać w innych DBMS z niewielkimi zmianami:

DELETE
FROM Table
WHERE Table.idTable IN  (  
    SELECT MAX(idTable)
    FROM idTable
    GROUP BY field1, field2, field3
    HAVING COUNT(*) > 1)

Zapomniałem powiedzieć, że to zapytanie nie usuwa wiersza o najniższym identyfikatorze zduplikowanych wierszy. Jeśli to zadziała, wypróbuj to zapytanie:

DELETE
FROM jobs
WHERE jobs.id IN  (  
    SELECT MAX(id)
    FROM jobs
    GROUP BY site_id, company, title, location
    HAVING COUNT(*) > 1)
Eduardo Rascon
źródło
To nie zadziała, jeśli będą więcej niż dwa duplikaty grupy.
OMG Ponies
11
Niestety, MySQL nie pozwala wybrać z tabeli, którą usuwaszERROR 1093: You can't specify target table 'Table' for update in FROM clause
Andomar
1
Aby rozwiązać "You can't specify target table 'Table' for update in FROM..."błąd, użyj: DELETE FROM Table WHERE Table.idTable IN ( SELECT MAX(idTable) FROM (SELECT * FROM idTable) AS tmp GROUP BY field1, field2, field3 HAVING COUNT(*) > 1)co zmusza MySQL do utworzenia tabeli tymczasowej. Jednak w dużych zestawach danych jest bardzo wolny ... w takich przypadkach polecam kod Andomara, który jest znacznie szybszy.
lepe
6

Szybszym sposobem jest wstawienie różnych wierszy do tabeli tymczasowej. Usunięcie duplikatów z tabeli zawierającej 8 milionów wierszy zajęło mi kilka godzin. Za pomocą wstawki i wyraźne zajęło to tylko 13 minut.

CREATE TABLE tempTableName LIKE tableName;  
CREATE INDEX ix_all_id ON tableName(cellId,attributeId,entityRowId,value);  
INSERT INTO tempTableName(cellId,attributeId,entityRowId,value) SELECT DISTINCT cellId,attributeId,entityRowId,value FROM tableName;  
TRUNCATE TABLE tableName;
INSERT INTO tableName SELECT * FROM tempTableName; 
DROP TABLE tempTableName;  
Nav
źródło
1
Twoja czwarta linia powinna powiedzieć, TRUNCATE TABLE tableNamea 5. linia powinna powiedziećINSERT INTO tableName SELECT * FROM tempTableName;
Sana
5

Rozwiązanie, które jest łatwe do zrozumienia i działa bez klucza podstawowego:

1) dodaj nową kolumnę logiczną

alter table mytable add tokeep boolean;

2) dodaj ograniczenie do zduplikowanych kolumn ORAZ nowej kolumny

alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);

3) ustaw kolumnę logiczną na true. To się powiedzie tylko w jednym ze zduplikowanych wierszy z powodu nowego ograniczenia

update ignore mytable set tokeep = true;

4) usuń wiersze, które nie zostały oznaczone jako Tokeep

delete from mytable where tokeep is null;

5) upuść dodaną kolumnę

alter table mytable drop tokeep;

Sugeruję zachowanie dodanego ograniczenia, aby w przyszłości uniknąć nowych duplikatów.

Xtian
źródło
1
To działało naprawdę dobrze w mysql 5.7, gdzie zaakceptowane rozwiązanie już nie działa
Robin31,
5

Usuń zduplikowane wiersze za pomocą instrukcji DELETE JOIN MySQL udostępnia instrukcję DELETE JOIN, której możesz użyć do szybkiego usuwania zduplikowanych wierszy.

Poniższa instrukcja usuwa zduplikowane wiersze i zachowuje najwyższy identyfikator:

DELETE t1 FROM contacts t1
    INNER JOIN
contacts t2 WHERE
t1.id < t2.id AND t1.email = t2.email;
Saad Mirza
źródło
5

Znalazłem prosty sposób. (zachowaj najnowszą wersję)

DELETE t1 FROM tablename t1 INNER JOIN tablename t2 
WHERE t1.id < t2.id AND t1.column1 = t2.column1 AND t1.column2 = t2.column2;
Rico Nguyen
źródło
4

Prosty i szybki dla wszystkich przypadków:

CREATE TEMPORARY TABLE IF NOT EXISTS _temp_duplicates AS (SELECT dub.id FROM table_with_duplications dub GROUP BY dub.field_must_be_uniq_1, dub.field_must_be_uniq_2 HAVING COUNT(*)  > 1);

DELETE FROM table_with_duplications WHERE id IN (SELECT id FROM _temp_duplicates);
artemiuz
źródło
Kod błędu: 1055. Wyrażenie nr 2 listy SELECT nie znajduje się w klauzuli GROUP BY i zawiera niezagregowaną kolumnę „dub.id”, która nie jest funkcjonalnie zależna od kolumn w klauzuli GROUP BY; to jest niezgodne z trybem sql = only_full_group_by
Swoogan
można wyłączyć „twardą kontrolę” za pomocą trybu sql_mode, patrz stackoverflow.com/questions/23921117/disable-only-full-group-by
artemiuz
4

Spowoduje to usunięcie duplikatów wierszy o takich samych wartościach dla tytułu, firmy i witryny. Pierwsze wystąpienie zostanie zachowane, a pozostałe kopie zostaną usunięte

DELETE t1 FROM tablename t1
INNER JOIN tablename t2 
WHERE 
    t1.id < t2.id AND
    t1.title = t2.title AND
    t1.company=t2.company AND
    t1.site_ID=t2.site_ID;
Dhrumil Panchal
źródło
jest wolny (5w + rzędy, limit czasu oczekiwania na blokadę), ale zadziałał
yurenchen
3

Odwiedzam tę stronę za każdym razem, gdy google „usuwam duplikaty z mysql”, ale dla moich rozwiązań THEIGNORE nie działają, ponieważ mam tabele mysql InnoDB

ten kod działa lepiej w dowolnym momencie

CREATE TABLE tableToclean_temp LIKE tableToclean;
ALTER TABLE tableToclean_temp ADD UNIQUE INDEX (fontsinuse_id);
INSERT IGNORE INTO tableToclean_temp SELECT * FROM tableToclean;
DROP TABLE tableToclean;
RENAME TABLE tableToclean_temp TO tableToclean;

tableToclean = nazwa tabeli, którą musisz wyczyścić

tableToclean_temp = tymczasowa tabela utworzona i usunięta

Francesco
źródło
2

To rozwiązanie przeniesie duplikaty do jednej tabeli, a unikaty do drugiej .

-- speed up creating uniques table if dealing with many rows
CREATE INDEX temp_idx ON jobs(site_id, company, title, location);

-- create the table with unique rows
INSERT jobs_uniques SELECT * FROM
    (
    SELECT * 
    FROM jobs
    GROUP BY site_id, company, title, location
    HAVING count(1) > 1
    UNION
    SELECT *
    FROM jobs
    GROUP BY site_id, company, title, location
    HAVING count(1) = 1
) x

-- create the table with duplicate rows
INSERT jobs_dupes 
SELECT * 
FROM jobs
WHERE id NOT IN
(SELECT id FROM jobs_uniques)

-- confirm the difference between uniques and dupes tables
SELECT COUNT(1)
AS jobs, 
(SELECT COUNT(1) FROM jobs_dupes) + (SELECT COUNT(1) FROM jobs_uniques)
AS sum
FROM jobs
Anthony Vipond
źródło
Dlaczego wziąłeś związek i nie tylko SELECT * FROM jobs GROUP BY site_id, company, title, location?
timctran
2

Począwszy od wersji 8.0 (2018), MySQL wreszcie obsługuje funkcje okien .

Funkcje okien są zarówno przydatne, jak i wydajne. Oto rozwiązanie, które pokazuje, jak ich użyć do rozwiązania tego zadania.

W podzapytaniu możemy użyć ROW_NUMBER()do przypisania pozycji do każdego rekordu w tabeli w column1/column2grupach, uporządkowanych według id. Jeśli nie ma duplikatów, rekord otrzyma numer wiersza 1. Jeśli duplikat istnieje, zostaną one ponumerowane rosnąco id(począwszy od1 ).

Po prawidłowym numerowaniu rekordów w podzapytaniu zewnętrzne zapytanie po prostu usuwa wszystkie rekordy, których numer wiersza nie jest równy 1.

Zapytanie:

DELETE FROM tablename
WHERE id IN (
    SELECT id
    FROM (
        SELECT 
            id, 
            ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id) rn
        FROM output
    ) t
    WHERE rn > 1
)
GMB
źródło
1

Aby usunąć duplikat rekordu z tabeli.

delete from job s 
where rowid < any 
(select rowid from job k 
where s.site_id = k.site_id and 
s.title = k.title and 
s.company = k.company);

lub

delete from job s 
where rowid not in 
(select max(rowid) from job k 
where s.site_id = k.site_id and
s.title = k.title and 
s.company = k.company);
Arun Solomon
źródło
1
-- Here is what I used, and it works:
create table temp_table like my_table;
-- t_id is my unique column
insert into temp_table (id) select id from my_table GROUP by t_id;
delete from my_table where id not in (select id from temp_table);
drop table temp_table;
Duy Hoang
źródło
0

Aby zduplikować rekordy z unikatowymi kolumnami, np. COL1, COL2, COL3, nie należy replikować (załóżmy, że pominięto 3 unikatowe kolumny w strukturze tabeli i do tabeli wprowadzono wiele zduplikowanych wpisów)

DROP TABLE TABLE_NAME_copy;
CREATE TABLE TABLE_NAME_copy LIKE TABLE_NAME;
INSERT INTO TABLE_NAME_copy
SELECT * FROM TABLE_NAME
GROUP BY COLUMN1, COLUMN2, COLUMN3; 
DROP TABLE TABLE_NAME;
ALTER TABLE TABLE_NAME_copy RENAME TO TABLE_NAME;

Nadzieja pomoże dev.

Abdul Rehman
źródło
0

TL; TR;

Dobrze opisany samouczek mający na celu rozwiązanie tego problemu można znaleźć na stronie mysqltutorial.org :

Jak usunąć zduplikowane wiersze w MySQL

Bardzo wyraźnie pokazano, jak usunąć duplikaty wierszy na trzy różne sposoby :

A) Za pomocą DELETE JOINinstrukcji

B) Korzystanie z tabeli pośredniej

C) Korzystanie z ROW_NUMBER()funkcji

Mam nadzieję, że to komuś pomoże.

simhumileco
źródło
0

Mam tabelę, która zapomina dodać klucz podstawowy w wierszu identyfikatora. Chociaż ma identyfikator auto_increment na id. Ale pewnego dnia jedna rzecz odtworzy dziennik bin mysql w bazie danych, który wstawia zduplikowane wiersze.

Usuwam zduplikowany wiersz o

  1. wybierz unikalne duplikaty wierszy i wyeksportuj je

select T1.* from table_name T1 inner join (select count(*) as c,id from table_name group by id) T2 on T1.id = T2.id where T2.c > 1 group by T1.id;

  1. usuń zduplikowane wiersze według identyfikatora

  2. wstaw wiersz z wyeksportowanych danych.

  3. Następnie dodaj klucz podstawowy do identyfikatora

Kris Roofe
źródło
-2

Lubię dokładniej określać, które rekordy usuwam, więc oto moje rozwiązanie:

delete
from jobs c1
where not c1.location = 'Paris'
and  c1.site_id > 64218
and exists 
(  
select * from jobs c2 
where c2.site_id = c1.site_id
and   c2.company = c1.company
and   c2.location = c1.location
and   c2.title = c1.title
and   c2.site_id > 63412
and   c2.site_id < 64219
)
Michael Tel
źródło
-4

Możesz łatwo usunąć zduplikowane rekordy z tego kodu.

$qry = mysql_query("SELECT * from cities");
while($qry_row = mysql_fetch_array($qry))
{
$qry2 = mysql_query("SELECT * from cities2 where city = '".$qry_row['city']."'");

if(mysql_num_rows($qry2) > 1){
    while($row = mysql_fetch_array($qry2)){
        $city_arry[] = $row;

        }

    $total = sizeof($city_arry) - 1;
        for($i=1; $i<=$total; $i++){


            mysql_query( "delete from cities2 where town_id = '".$city_arry[$i][0]."'");

            }
    }
    //exit;
}
Syed Amir Bukhari
źródło
3
Jest to bardzo zła forma - zadania bazy danych powinny być wykonywane w DB, gdzie są one znacznie szybsze, zamiast ciągłego przesyłania danych między php / mysql, ponieważ znasz jedno lepiej niż drugie.
Maks.
-4

Musiałem to zrobić z polami tekstowymi i natrafiłem na limit 100 bajtów w indeksie.

Rozwiązałem to, dodając kolumnę, wykonując skrót pól md5 i wykonując alter.

ALTER TABLE table ADD `merged` VARCHAR( 40 ) NOT NULL ;
UPDATE TABLE SET merged` = MD5(CONCAT(`col1`, `col2`, `col3`))
ALTER IGNORE TABLE table ADD UNIQUE INDEX idx_name (`merged`);
Sunil
źródło