Поиск повторяющихся значений в MySQL

665

У меня есть таблица с столбцом varchar, и я бы хотел найти все записи, которые имеют повторяющиеся значения в этом столбце. Какой лучший запрос я могу использовать для поиска дубликатов?

  • 0
    Поскольку вы упомянули найти все записи, я предполагаю, что вам нужно знать КЛЮЧИ, а также дублированные значения в этой колонке varchar.
  • 0
    Я могу найти ключи достаточно легко после того, как получу значения, я просто хочу получить список всех повторяющихся значений.
Теги:

24 ответа

1380
Лучший ответ

Сделайте SELECT с предложением GROUP BY. Пусть говорят, что имя - это столбец, в котором вы хотите найти дубликаты:

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

Это вернет результат с именем в первом столбце и количеством раз, сколько раз это значение появляется во второй.

  • 19
    Но как это полезно, если вы не можете получить идентификаторы строк с повторяющимися значениями? Да, вы можете выполнить новый запрос на сопоставление для каждого дублированного значения, но возможно ли просто перечислить дубликаты?
  • 15
    @NobleUplift Вы можете сделать GROUP_CONCAT(id) и он будет перечислять идентификаторы. Смотрите мой ответ для примера.
Показать ещё 7 комментариев
204
SELECT varchar_col
FROM table
GROUP BY varchar_col
HAVING COUNT(*) > 1;
  • 9
    Превосходит ответ @ levik, так как не добавляет лишний столбец. Делает его полезным для использования с IN() / NOT IN() .
  • 1
    Поиск дублирующих записей в нескольких столбцах в MySQL activelab.io/code-snippets/… Я нашел этот сайт полезным.
153
SELECT  *
FROM    mytable mto
WHERE   EXISTS
        (
        SELECT  1
        FROM    mytable mti
        WHERE   mti.varchar_column = mto.varchar_column
        LIMIT 1, 1
        )

Этот запрос возвращает полные записи, а не только разные varchar_column.

Этот запрос не использует COUNT(*). Если имеется много дубликатов, COUNT(*) стоит дорого, и вам не нужен весь COUNT(*), вам просто нужно знать, есть ли две строки с одинаковым значением.

Наличие индекса на varchar_column, конечно, значительно ускорит этот запрос.

  • 3
    Отлично. Я добавил ORDER BY varchar_column DESC в конец запроса.
  • 4
    Это должен быть принятый ответ, поскольку GROUP BY и HAVING возвращают только один из возможных дубликатов. Кроме того, производительность с индексированным полем вместо COUNT(*) и возможность ORDER BY для группировки дубликатов записей.
Показать ещё 7 комментариев
109

Построение ответа levik для получения идентификаторов повторяющихся строк, вы можете сделать GROUP_CONCAT, если ваш сервер поддерживает его (это вернет список идентификаторов, разделенных запятыми).

SELECT GROUP_CONCAT(id), name, COUNT(*) c FROM documents GROUP BY name HAVING c > 1;
  • 10
    Все это время, не зная о GROUP_CONCAT ()! очень очень полезно
  • 0
    Действительно ценится Мэтт. Это действительно полезно! Для тех, кто пытается обновить в phpmyadmin, если вы оставляете идентификатор вместе с функцией, подобной этой: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...] он включает встроенное редактирование и должен обновлять все строки участвует (или, по крайней мере, первое совпадение), но, к сожалению, редактирование приводит к ошибке Javascript ...
Показать ещё 3 комментария
12
SELECT * 
FROM `dps` 
WHERE pid IN (SELECT pid FROM `dps` GROUP BY pid HAVING COUNT(pid)>1)
  • 1
    Нет, потому что это, пожалуй, самый медленный из всех. Подвыборы известны своей медлительностью, так как они выполняются для каждой возвращаемой строки.
10

Предполагая, что ваша таблица имеет имя TableABC, а нужный столбец - Col, а первичный ключ - T1 - Key.

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

Преимущество такого подхода в отношении вышеприведенного ответа заключается в том, что он дает ключ.

  • 3
    +1 Потому что это удобно. Хотя, как ни странно, сам результат содержит дубликаты (в нем перечислены a и b, затем b и a.)
  • 1
    @FabienSnauwaert Вы можете избавиться от некоторых дубликатов, сравнив меньше (или больше чем)
9

Чтобы узнать, сколько записей дубликатов в столбце имен в Employee, приведенный ниже запрос полезен;

Select name from employee group by name having count(*)>1;
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

Замените город на таблицу. Замените имя на имя своего поля

6

Я не вижу никаких подходов JOIN, у которых есть много применений с точки зрения дубликатов.

Этот подход дает вам реальные удвоенные результаты.

SELECT t1.* FROM table as t1 LEFT JOIN table as t2 ON t1.name=t2.name and t1.id!=t2.id WHERE t2.id IS NOT NULL ORDER BY t1.name
  • 1
    К вашему сведению - вы захотите «выбрать отличный somecol ..», если существует вероятность существования более 1 дублированной записи, иначе результаты будут содержать дубликаты найденных дублированных строк.
5

В моем последнем запросе было несколько ответов, которые помогли - объединить группу по, count и GROUP_CONCAT.

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

Это обеспечивает идентификатор обоих примеров (разделенных запятыми), штрих-кода, который мне нужен, и количества дубликатов.

Соответственно измените таблицу и столбцы.

4

Я видел вышеприведенный результат, и запрос будет работать нормально, если вам нужно проверить одно значение столбца, которое является дубликатом. Например, электронная почта.

Но если вам нужно проверить с большим количеством столбцов и хотите проверить комбинацию результата, чтобы этот запрос работал нормально:

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)
3

Взяв @maxyfc ответ, мне нужно было найти все строки, которые были возвращены с повторяющимися значениями, поэтому я мог бы редактировать их в MySQL Workbench:

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

Ниже вы найдете все product_id, которые используются более одного раза. Вы получаете только одну запись для каждого product_id.

SELECT product_id FROM oc_product_reward GROUP BY product_id HAVING count( product_id ) >1

Код, взятый из: http://chandreshrana.blogspot.in/2014/12/find-duplicate-records-based-on-any.html

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

INSERT INTO tbl_master
    (`id`, `email`) VALUES
    (1, '[email protected]'),
    (2, '[email protected]'),
    (3, '[email protected]'),
    (4, '[email protected]'),
    (5, '[email protected]');

QUERY : SELECT id, email FROM tbl_master
WHERE email IN (SELECT email FROM tbl_master GROUP BY email HAVING COUNT(id) > 1)
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
    Выполнение одного и того же подзапроса дважды кажется неэффективным.
2
SELECT DISTINCT a.email FROM `users` a LEFT JOIN `users` b ON a.email = b.email WHERE a.id != b.id;
  • 1
    Стоит отметить, что это невыносимо медленно или может даже не закончиться, если запрашиваемый столбец не проиндексирован. В противном случае я смог изменить a.email на a.* И получить все идентификаторы строк с дубликатами.
  • 0
    @NobleUplift О чем ты говоришь?
Показать ещё 3 комментария
1

Если вы хотите удалить дубликаты, используйте DISTINCT

В противном случае используйте этот запрос:

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

1

Я предпочитаю использовать оконные функции (MySQL 8. 0+) для поиска дубликатов, потому что я мог видеть всю строку:

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

1

Один очень поздний вклад... в случае, если он помогает любому waaaaaay вниз по линии... У меня была задача найти соответствующие пары транзакций (на самом деле обе стороны передачи от учетной записи к учетной записи) в банковском приложении, чтобы определите, какие из них были "от" и "до" для каждой транзакции между счетами, поэтому мы закончили с этим:

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;

В результате DuplicateResultsTable содержит строки, содержащие совпадающие (т.е. повторяющиеся) транзакции, но он также предоставляет одинаковый идентификатор транзакции в обратном порядке во второй раз, когда он соответствует одной и той же паре, поэтому внешний SELECT существует для группировки по первому идентификатору транзакции, который выполняется с помощью LEAST и GREATEST, чтобы убедиться, что два транзагента всегда находятся в одном и том же порядке в результатах, что делает его безопасным для GROUP первым, тем самым устраняя все дубликаты матчей. Прошел почти миллион записей и определил 12 000+ матчей всего за 2 секунды. Конечно, transactionid - это основной индекс, который действительно помог.

1

Для удаления повторяющихся строк с несколькими полями сначала привяжите их к новому уникальному ключу, который указан только для отдельных строк, а затем используйте команду "group by" для удаления повторяющихся строк с помощью того же нового уникального ключа:

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;
  • 0
    Вы также можете добавить объяснение?
  • 0
    Почему бы не использовать CREATE TEMPORARY TABLE ... ? Небольшое объяснение вашего решения было бы здорово.
1
Select column_name, column_name1,column_name2, count(1) as temp from table_name group by column_name having temp > 1
0

Попробуйте использовать этот запрос:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
0
SELECT DISTINCT name, count(name) as times FROM yourtable GROUP BY name
0
SELECT ColumnA, COUNT( * )
FROM Table
GROUP BY ColumnA
HAVING COUNT( * ) > 1
  • 3
    Это неверно, так как он также находит уникальные вхождения. 0 должно быть 1.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню