Znajdowanie zduplikowanych wartości w MySQL


769

Mam tabelę z kolumną varchar i chciałbym znaleźć wszystkie rekordy, które mają zduplikowane wartości w tej kolumnie. Jakiego zapytania najlepiej użyć do znalezienia duplikatów?


1
Ponieważ wspomniano o znajdowaniu wszystkich rekordów, zakładam, że musisz znać KLUCZE, a także zduplikowane WARTOŚCI w tej kolumnie varchar.
TechTravelThink

Mogę znaleźć klucze dość łatwo po otrzymaniu wartości, naprawdę chcę tylko listę wszystkich zduplikowanych wartości.
Jon Tackabury

Odpowiedzi:


1521

Zrób SELECTz GROUP BYklauzulą. Powiedzmy, że nazwa to kolumna, w której chcesz znaleźć duplikaty:

SELECT name, COUNT(*) c FROM table GROUP BY name HAVING c > 1;

Zwróci to wynik z wartością nazwy w pierwszej kolumnie i liczbą wyświetleń tej wartości w drugiej kolumnie.


27
Ale w jaki sposób jest to przydatne, jeśli nie można uzyskać identyfikatorów wierszy ze zduplikowanymi wartościami? Tak, możesz wykonać nowe zapytanie pasujące do każdej zduplikowanej wartości, ale czy można po prostu podać listę duplikatów?
NobleUplift

23
@NobleUplift Możesz to zrobić, GROUP_CONCAT(id)a wyświetli się lista identyfikatorów. Zobacz moją odpowiedź na przykład.
Matt Rardon

5
Co by to znaczyło, gdyby powiedział ERROR: column "c" does not exist LINE 1?
Użytkownik

15
Jestem zdezorientowany, dlaczego jest to zaakceptowana odpowiedź i dlaczego ma tak wiele pozytywnych opinii. OP zapytał: „Chciałbym znaleźć wszystkie rekordy, które mają zduplikowane wartości w tej kolumnie”. Ta odpowiedź zwraca tabelę zliczeń. -1
Monica Heddneck

4
Dla tych, którzy nie rozumieją, jak działa HAVING - jest to po prostu filtr zestawu wyników, więc dzieje się to po głównym zapytaniu.
John Hunt

236
SELECT varchar_col
FROM table
GROUP BY varchar_col
HAVING COUNT(*) > 1;

10
Lepsza niż odpowiedź @ levik, ponieważ nie dodaje dodatkowej kolumny. Sprawia, że ​​jest użyteczny w połączeniu z IN()/ NOT IN().
wmassingham

172
SELECT  *
FROM    mytable mto
WHERE   EXISTS
        (
        SELECT  1
        FROM    mytable mti
        WHERE   mti.varchar_column = mto.varchar_column
        LIMIT 1, 1
        )

To zapytanie zwraca pełne rekordy, a nie tylko odrębne varchar_column.

To zapytanie nie używa COUNT(*). Jeśli jest wiele duplikatów, COUNT(*)jest kosztowne i nie potrzebujesz całości COUNT(*), musisz tylko wiedzieć, czy są dwa wiersze o tej samej wartości.

Posiadanie indeksu varchar_columnprzyspieszy oczywiście to zapytanie.


3
Bardzo dobre. Dodałem ORDER BY varchar_column DESCna końcu zapytania.
trante

8
To powinien być zaakceptowany odpowiedź, jak GROUP BYi HAVINGpowraca tylko jeden z możliwych duplikatów. Ponadto wydajność z polem indeksowanym zamiast COUNT(*)oraz możliwość ORDER BYgrupowania zduplikowanych rekordów.
Rémi Breton,

1
Jak stwierdzono w powyższych komentarzach, to zapytanie pozwala wyświetlić listę wszystkich zduplikowanych wierszy. Bardzo przydatne.
TryHarder

4
Patrząc na to, nie rozumiem, jak by to działało. Czy wewnętrzny warunek nie zawsze będzie prawdziwy, ponieważ jakikolwiek rząd w zewnętrznym stole będzie również dostępny w wewnętrznym stole, a więc każdy rząd zawsze będzie przynajmniej taki sam? Próbowałem zapytania i uzyskałem wynik, który podejrzewałem - zwracany był każdy wiersz. Ale przy tak wielu pozytywnych opiniach wątpię w siebie. Czy w wewnętrznym zapytaniu brakuje czegoś takiego jak „AND mto.id <> mti.id”? Gdy to dodam, działa dla mnie.
Clox,

2
@Quassnoi W porządku. Próbowałem umieścić go na sqlfiddle, ale poddałem się, ponieważ każde zapytanie, które próbuję uruchomić, oprócz tworzenia schematu, wygasło. Doszedłem do wniosku, że samo usunięcie „EXISTS” sprawia, że ​​zapytanie działa dla mnie poprawnie.
Clox

144

Opierając się na odpowiedzi Levika, aby uzyskać identyfikatory zduplikowanych wierszy, możesz zrobić a, GROUP_CONCATjeśli Twój serwer to obsługuje (zwróci listę identyfikatorów oddzielonych przecinkami).

SELECT GROUP_CONCAT(id), name, COUNT(*) c FROM documents GROUP BY name HAVING c > 1;

12
Cały czas nie wiedząc o GROUP_CONCAT ()! bardzo, bardzo przydatne.
aesede

Naprawdę doceniany Matt. To jest naprawdę pomocne! Dla tych, którzy próbują zaktualizować w phpmyadmin, jeśli zostawisz identyfikator razem z funkcją w ten sposób: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]umożliwia on edycję bezpośrednią i powinien zaktualizować wszystkie zaangażowane wiersze (lub przynajmniej pierwszy dopasowany), ale niestety edycja generuje błąd JavaScript. ..
Armfoot

Jak byś wtedy obliczył, ile identyfikatorów podlega duplikacji?
CMCDragonkai

2
Jak mogę nie zgrupować wszystkich identyfikatorów, ale zamiast tego wymienić je od pierwszego do ostatniego; ze wszystkimi odpowiednimi wartościami w kolumnach obok nich? Zamiast grupować, pokazuje tylko ID 1 i jego wartość, ID 2 i jego wartość. NAWET, jeśli wartości identyfikatora są takie same.
MailBlade,

1
Niezwykle pomocna odpowiedź, powinna być na górze, aby więcej osób ją zobaczyło. Pamiętam, jak wiele bólu przeżyłem, tworząc takie listy, i było ono dostępne przez cały czas jako polecenie.
John

13

Zakładając, że twoja tabela ma nazwę TableABC, a kolumna, którą chcesz, to Col, a klucz podstawowy do T1 to Key.

SELECT a.Key, b.Key, a.Col 
FROM TableABC a, TableABC b
WHERE a.Col = b.Col 
AND a.Key <> b.Key

Przewagą tego podejścia nad powyższą odpowiedzią jest to, że daje on Klucz.


4
+1 Ponieważ jest to przydatne. Chociaż, jak na ironię, sam wynik zawiera duplikaty (wymienia aib, a następnie
bib

2
@FabienSnauwaert Możesz pozbyć się niektórych duplikatów, porównując mniej niż (lub więcej niż)
Michael

@TechTravel Pomyśl, że twoja odpowiedź jest bardzo jasna, dziękuję za to, ale na dużym stole zajmuje to trochę czasu (około 2 mln na więcej 20 000 wpisów w tabeli), a po pokazaniu 25 pierwszych wyników, jeśli kliknę, aby wyświetlić następny, phpmyadmin pokaż błąd "# 1052 - Kolumna „id” w klauzuli kolejności jest niejednoznaczna ”
bcag2

12
SELECT * 
FROM `dps` 
WHERE pid IN (SELECT pid FROM `dps` GROUP BY pid HAVING COUNT(pid)>1)

1
Nie, ponieważ jest to prawdopodobnie najwolniejszy z całej serii. Podselekcje są notorycznie wolne, ponieważ są wykonywane dla każdego zwracanego wiersza.
Oddman

10

Aby dowiedzieć się, ile rekordów jest duplikatami w kolumnie z nazwami w Pracowniku, pomocne jest poniższe zapytanie;

Select name from employee group by name having count(*)>1;

10

aby uzyskać wszystkie dane, które zawierają duplikację, użyłem tego:

SELECT * FROM TableName INNER JOIN(
  SELECT DupliactedData FROM TableName GROUP BY DupliactedData HAVING COUNT(DupliactedData) > 1 order by DupliactedData)
  temp ON TableName.DupliactedData = temp.DupliactedData;

TableName = tabela, z którą pracujesz.

DupliactedData = zduplikowane dane, których szukasz.


Ten pokazuje każdy duplikat we własnym wierszu. To jest to, czego potrzebuję. Dzięki.
ciepły biały

8

Moje ostatnie zapytanie zawierało kilka odpowiedzi, które pomogły - łączenie grupy według, liczenia i GROUP_CONCAT.

SELECT GROUP_CONCAT(id), `magento_simple`, COUNT(*) c 
FROM product_variant 
GROUP BY `magento_simple` HAVING c > 1;

Podaje to identyfikator obu przykładów (oddzielonych przecinkami), potrzebny kod kreskowy i liczbę duplikatów.

Zmień odpowiednio tabelę i kolumny.


8

Nie widzę żadnych podejść do JOIN, które mają wiele zastosowań pod względem duplikatów.

Takie podejście daje rzeczywiste podwojone wyniki.

SELECT t1.* FROM my_table as t1 
LEFT JOIN my_table as t2 
ON t1.name=t2.name and t1.id!=t2.id 
WHERE t2.id IS NOT NULL 
ORDER BY t1.name

2
FYI - Będziesz chciał „wybrać wyraźne somecol ..”, jeśli istnieje możliwość istnienia więcej niż 1 zduplikowanego rekordu, w przeciwnym razie wyniki będą zawierać duplikaty znalezionych duplikatów.
Drew

7
SELECT t.*,(select count(*) from city as tt
  where tt.name=t.name) as count
  FROM `city` as t
  where (
     select count(*) from city as tt
     where tt.name=t.name
  ) > 1 order by count desc

Zastąp miasto swoim stołem. Zastąp nazwę swoją nazwą pola


7

Biorąc dalej odpowiedź @ maxyfc , musiałem znaleźć wszystkie wiersze, które zostały zwrócone ze zduplikowanymi wartościami, aby móc je edytować w MySQL Workbench :

SELECT * FROM table
   WHERE field IN (
     SELECT field FROM table GROUP BY field HAVING count(*) > 1
   ) ORDER BY field

6

Widziałem powyższy wynik i zapytanie będzie działać poprawnie, jeśli chcesz sprawdzić wartość jednej kolumny, która jest zduplikowana. Na przykład e-mail.

Ale jeśli musisz sprawdzić więcej kolumn i chcesz sprawdzić kombinację wyniku, aby zapytanie działało poprawnie:

SELECT COUNT(CONCAT(name,email)) AS tot,
       name,
       email
FROM users
GROUP BY CONCAT(name,email)
HAVING tot>1 (This query will SHOW the USER list which ARE greater THAN 1
              AND also COUNT)

Dokładnie to, co było potrzebne! Oto moje zapytanie, sprawdzanie 3 pól pod kątem duplikatów:SELECT COUNT(CONCAT(userid,event,datetime)) AS total, userid, event, datetime FROM mytable GROUP BY CONCAT(userid, event, datetime ) HAVING total>1
Kai Noack

4

Wolę używać funkcji okienkowych (MySQL 8.0+) do znajdowania duplikatów, ponieważ widziałem cały wiersz:

WITH cte AS (
  SELECT *
    ,COUNT(*) OVER(PARTITION BY col_name) AS num_of_duplicates_group
    ,ROW_NUMBER() OVER(PARTITION BY col_name ORDER BY col_name2) AS pos_in_group
  FROM table
)
SELECT *
FROM cte
WHERE num_of_duplicates_group > 1;

DB Fiddle Demo


3
SELECT 
    t.*,
    (SELECT COUNT(*) FROM city AS tt WHERE tt.name=t.name) AS count 
FROM `city` AS t 
WHERE 
    (SELECT count(*) FROM city AS tt WHERE tt.name=t.name) > 1 ORDER BY count DESC

1
Dwukrotne wykonanie tego samego podzapytania wydaje się nieefektywne.
NobleUplift


3
CREATE TABLE tbl_master
    (`id` int, `email` varchar(15));

INSERT INTO tbl_master
    (`id`, `email`) VALUES
    (1, 'test1@gmail.com'),
    (2, 'test2@gmail.com'),
    (3, 'test1@gmail.com'),
    (4, 'test2@gmail.com'),
    (5, 'test5@gmail.com');

QUERY : SELECT id, email FROM tbl_master
WHERE email IN (SELECT email FROM tbl_master GROUP BY email HAVING COUNT(id) > 1)

2
SELECT DISTINCT a.email FROM `users` a LEFT JOIN `users` b ON a.email = b.email WHERE a.id != b.id;

1
Warto zauważyć, że jest to nieznośnie powolne lub może nawet nie zakończyć się, jeśli kolumna, o którą pytamy, nie jest indeksowana. W przeciwnym razie, byłem w stanie zmienić a.emailsię a.*i uzyskać wszystkie identyfikatory wierszy z duplikatów.
NobleUplift

@NobleUplift O czym ty mówisz?
Michael

@Michael Cóż, ponieważ ma on trzy lata, nie mogę przetestować jakiejkolwiek wersji MySQL, której używałem, ale próbowałem tego samego zapytania w bazie danych, w której wybrana kolumna nie miała indeksu, więc zajęło mi to sporo kilka sekund do końca. Zmiana na SELECT DISTINCT a.*prawie natychmiastowa.
NobleUplift,

@NobleUplift Ah ok. Rozumiem, że jest powolny ... martwi mnie to, że „może nawet nie skończyć”.
Michael

@Michael Nie pamiętam, na której tabeli w naszym systemie musiałem uruchomić tę kwerendę, ale dla tych z kilkoma milionami rekordów prawdopodobnie by się skończyły, ale w czasie, który trwał tak długo, że przestałem patrzeć, kiedy faktycznie by się skończyło.
NobleUplift,

1

Aby usunąć zduplikowane wiersze z wieloma polami, najpierw anuluj je do nowego unikalnego klucza określonego dla jedynych odrębnych wierszy, a następnie użyj polecenia „grupuj”, aby usunąć zduplikowane wiersze z tym samym nowym unikalnym kluczem:

Create TEMPORARY table tmp select concat(f1,f2) as cfs,t1.* from mytable as t1;
Create index x_tmp_cfs on tmp(cfs);
Create table unduptable select f1,f2,... from tmp group by cfs;

czy możesz również dodać wyjaśnienie?
Robert

Dlaczego nie użyć CREATE TEMPORARY TABLE ...? Świetne byłoby krótkie wyjaśnienie twojego rozwiązania.
maxhb

1

Jeden bardzo spóźniony wkład ... na wypadek, gdyby to pomogło każdemu zejść w dół linii ... Miałem zadanie znaleźć pasujące pary transakcji (właściwie obie strony przelewów między rachunkami) w aplikacji bankowej, aby zidentyfikować, które z nich były „z” i „do” dla każdej transakcji przeniesienia między kontami, więc skończyliśmy na tym:

SELECT 
    LEAST(primaryid, secondaryid) AS transactionid1,
    GREATEST(primaryid, secondaryid) AS transactionid2
FROM (
    SELECT table1.transactionid AS primaryid, 
        table2.transactionid AS secondaryid
    FROM financial_transactions table1
    INNER JOIN financial_transactions table2 
    ON table1.accountid = table2.accountid
    AND table1.transactionid <> table2.transactionid 
    AND table1.transactiondate = table2.transactiondate
    AND table1.sourceref = table2.destinationref
    AND table1.amount = (0 - table2.amount)
) AS DuplicateResultsTable
GROUP BY transactionid1
ORDER BY transactionid1;

W rezultacie DuplicateResultsTablewiersze zawierające pasujące (tj. Zduplikowane) transakcje zapewniają również te same identyfikatory transakcji w odwrotnej kolejności za drugim razem, gdy dopasowuje tę samą parę, więc element zewnętrzny SELECTmoże grupować według pierwszego identyfikatora transakcji, który jest wykonywany za pomocą LEASTi, GREATESTaby upewnić się, że dwa transakcje są zawsze w tej samej kolejności w wynikach, dzięki czemu jest bezpieczny GROUPprzez pierwszy, eliminując w ten sposób wszystkie duplikaty dopasowań. Przejrzał prawie milion rekordów i zidentyfikował ponad 12 000 meczów w niecałe 2 sekundy. Oczywiście transakcja jest głównym indeksem, który naprawdę pomógł.


1
Select column_name, column_name1,column_name2, count(1) as temp from table_name group by column_name having temp > 1

1
SELECT ColumnA, COUNT( * )
FROM Table
GROUP BY ColumnA
HAVING COUNT( * ) > 1

3
Jest to niepoprawne, ponieważ znajduje również unikalne zdarzenia. 0 powinno wynosić 1.
Kafoso

1

Jeśli chcesz usunąć zduplikowane użycie DISTINCT

W przeciwnym razie użyj tego zapytania:

SELECT users.*,COUNT(user_ID) as user FROM users GROUP BY user_name HAVING user > 1;


0

Spróbuj użyć tego zapytania:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.