Знайдіть дублікати записів у MySQL


650

Я хочу витягнути дублікати записів у базі даних MySQL. Це можна зробити за допомогою:

SELECT address, count(id) as cnt FROM list
GROUP BY address HAVING cnt > 1

Результати:

100 MAIN ST    2

Я хотів би витягнути його так, щоб він показував, що кожен рядок є дублікатом. Щось на зразок:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

Будь-які думки про те, як це можна зробити? Я намагаюся уникати першого, потім шукаю дублікати з другим запитом у коді.

Відповіді:


684

Ключ полягає в тому, щоб переписати цей запит, щоб він міг використовуватися як підзапит.

SELECT firstname, 
   lastname, 
   list.address 
FROM list
   INNER JOIN (SELECT address
               FROM   list
               GROUP  BY address
               HAVING COUNT(id) > 1) dup
           ON list.address = dup.address;

69
Будьте уважні до підзапитів. Підзапроси є / можуть бути смішно поганими для продуктивності. Якщо це трапляється часто і / або з великою кількістю повторюваних записів, я б міг перенести обробку з бази даних і в набір даних.
bdwakefield

11
Це некорельований підзапит, тому він не повинен бути занадто поганим, якщо припустити, що один запит не є погано розробленим.
ʞɔıu

Прекрасна. Здогадайтесь, це синтаксис навколо "ПОМИЛКА 1248 (42000): Кожна отримана таблиця повинна мати свій псевдонім"
doublejosh

3
Це правильна ідея, але знову ж таки, як нижче, це працює лише в тому випадку, якщо адреси гарантовано стандартизовані ...
Метт,

30
+1 за допомогою цього запиту, ви можете знайти дублікати, а також триплікати, чотиризначні ..... і так далі
albanx

352
SELECT date FROM logs group by date having count(*) >= 2

5
Це був найпростіший робочий запит для використання з Laravel. Просто потрібно було додати ->having(DB::raw('count(*)'), '>', 2)запит. Дуже дякую!
Кова

1
Добре працює з 10 мільйонів таблиць рядків. Це має бути найкраща відповідь
Террі Лін

13
Будьте обережні з цією відповіддю. Він повертає лише один із дублікатів. Якщо у вас є більше 2-х примірників одного і того ж запису, ви не побачите їх усіх, і після видалення запису, який повернувся, у вашій таблиці все одно будуть дублікати.
Мікіко Джейн

7
Чому >=2? Просто використовуйтеHAVING COUNT(*) > 1
BadHorsie

2
@ TerryLin Враховуючи, що це насправді не вирішує спочатку заявлену проблему (як було повернути всі дублікати), я не згоден.
Майкл

198

Чому б просто НЕ ВНУТРІШНЯ приєднати стіл до себе?

SELECT a.firstname, a.lastname, a.address
FROM list a
INNER JOIN list b ON a.address = b.address
WHERE a.id <> b.id

DISTINCT потрібен, якщо адреса може існувати більше двох разів.


20
Я занадто тестував це, і це було майже в 6 разів повільніше порівняно з прийнятим в моїй ситуації рішенням (остання MySQL, таблиця на 120 000 рядків). Це може бути пов’язано з тим, що потрібна тимчасова таблиця, запустіть ПОЯСНЕННЯ на обох, щоб побачити відмінності.

4
Я змінив останню частину запиту, щоб WHERE a.id > b.idвідфільтрувати лише нові дублікати, таким чином я можу зробити DELETEбезпосередньо результат. Переключіть порівняння, щоб перелічити старіші дублікати.
Стофф

1
На це пішло 50 секунд, а відповідь @ doublejosh зайняла .13 секунд.
antonagestam

Треба додати, що ця відповідь дає дублюючі відповіді, незважаючи на те, де БЕЗ, як у випадку, коли одна адреса потроїться, вихідні рядки подвоюються. Якщо це вчетверо, я вважаю, що відповідь буде втричі.
Wli

Я перевірив це в leetcode " leetcode.com/problems/duplicate-emails ". Це було швидше порівняно з підзапитом.
вал

56

Я спробував найкращу відповідь, обрану на це питання, але це мене дещо збентежило. Мені це справді було потрібно лише на одному полі зі свого столу. Наступний приклад із цього посилання дуже добре розробив для мене:

SELECT COUNT(*) c,title FROM `data` GROUP BY title HAVING c > 1;

Працює як шарм!
Вініцій

47
select `cityname` from `codcities` group by `cityname` having count(*)>=2

Це аналогічний запит, який ви запитували, і його 200% робочого та простого. Насолоджуйтесь !!!


37

Чи не так просто:

SELECT *
FROM tc_tariff_groups
GROUP BY group_id
HAVING COUNT(group_id) >1

?


1
працював для мене там, де мені довелося просто обробити ~ 10 000 дублікатів рядків, щоб зробити їх унікальними, набагато швидшими, ніж завантажити всі 600 000 рядків.
adrianTNT

1
набагато простіше
Швед

35

Знайдіть дублюючих користувачів за електронною адресою за допомогою цього запиту ...

SELECT users.name, users.uid, users.mail, from_unixtime(created)
FROM users
INNER JOIN (
  SELECT mail
  FROM users
  GROUP BY mail
  HAVING count(mail) > 1
) dupes ON users.mail = dupes.mail
ORDER BY users.mail;

2
Щоб знайти фактичний дублікат, потрібен лише внутрішній запит. Це швидше, ніж інші відповіді.
antonagestam

20

ми можемо знайти дублікати, залежить від кількох полів. Для цих випадків ви можете використовувати формат нижче.

SELECT COUNT(*), column1, column2 
FROM tablename
GROUP BY column1, column2
HAVING COUNT(*)>1;

16

Пошук дублікатів адрес набагато складніший, ніж здається, особливо якщо вам потрібна точність. У цьому випадку запиту MySQL недостатньо ...

Я працюю в SmartyStreets , де ми займаємось перевіркою та дедуплікацією та іншим матеріалом, і я бачив багато різноманітних проблем із подібними проблемами.

Є кілька сторонніх служб, які позначать дублікати у списку для вас. Виконуючи це виключно за допомогою запиту MySQL, не враховуватимуться відмінності у форматах адрес та стандартах. USPS (для американської адреси) має певні вказівки щодо створення цих стандартів, але лише кілька постачальників сертифіковані на виконання таких операцій.

Отже, я б рекомендував найкращу відповідь для вас - експортувати таблицю, наприклад, у файл CSV, та подати її на здатний процесор списку. Одним із таких є інструмент перевірки масових адрес SmartyStreets, який автоматично зробить вас за кілька секунд до декількох хвилин. Він позначить повторювані рядки з новим полем під назвою "Дублікат" та значенням Yу ньому.


6
+1 - бачити труднощі, пов’язані з узгодженням рядків адреси, хоча ви можете уточнити, що питання "копії записів" ОП само по собі не є складним, але це при порівнянні адрес
історія

13

Іншим рішенням буде використання псевдонімів таблиці, наприклад:

SELECT p1.id, p2.id, p1.address
FROM list AS p1, list AS p2
WHERE p1.address = p2.address
AND p1.id != p2.id

Все, що ви насправді робите в цьому випадку, - це взяти оригінальну таблицю списку , створити дві таблиці p retend - p 1 та p 2 - із цього, а потім виконати з'єднання в стовпці адреси (рядок 3). Четвертий рядок гарантує, що однаковий запис не відображається кілька разів у вашому наборі результатів ("копії дублікатів").


1
Добре працює. Якщо ДЕЙ перевіряє LIKE, то знайдеться і апострофи. Робить запит повільніше, але в моєму випадку це одноразовий.
плітки

10

Не буде дуже ефективним, але це має працювати:

SELECT *
FROM list AS outer
WHERE (SELECT COUNT(*)
        FROM list AS inner
        WHERE inner.address = outer.address) > 1;

10

Це вибере дублікати в одному проході таблиці, без підзапитів.

SELECT  *
FROM    (
        SELECT  ao.*, (@r := @r + 1) AS rn
        FROM    (
                SELECT  @_address := 'N'
                ) vars,
                (
                SELECT  *
                FROM
                        list a
                ORDER BY
                        address, id
                ) ao
        WHERE   CASE WHEN @_address <> address THEN @r := 0 ELSE 0 END IS NOT NULL
                AND (@_address := address ) IS NOT NULL
        ) aoo
WHERE   rn > 1

Цей запит фактично емулюється ROW_NUMBER()вOracle таSQL Server

Детальніше дивіться у статті в моєму блозі:


20
Не для нитпика, але FROM (SELECT ...) aooце запит :-P
Rocket Hazmat

8

Це також покаже вам кількість дублікатів і замовить результати без приєднання

SELECT  `Language` , id, COUNT( id ) AS how_many
FROM  `languages` 
GROUP BY  `Language` 
HAVING how_many >=2
ORDER BY how_many DESC

удосконалювати , тому що вона по- , як і раніше говорить , скільки записів продубльовані
DENIS

4
 SELECT firstname, lastname, address FROM list
 WHERE 
 Address in 
 (SELECT address FROM list
 GROUP BY address
 HAVING count(*) > 1)

Спробував і цей, але, здається, просто висить. Повірте, повернення з внутрішнього запиту не відповідає формату параметра IN.
doublejosh

Що ти не означає, що не відповідає формату параметрів? Все, що потребує IN, - це те, що ваш підзапит повинен повернути один стовпець. Це дійсно досить просто. Більш ймовірно, що ваш підзапит генерується на стовпці, який не індексується, тому для запуску потрібна непомірна кількість часу. Я б порадив, якщо потрібно тривати довгий час, щоб розбити його на два запити. Візьміть підзапит, запустіть його спочатку у тимчасову таблицю, створіть на ньому індекс, а потім запустіть повний запит, виконуючи підзапит, де ваше дублікат поля у тимчасовій таблиці.
Райан Ропер

Я хвилювався, що IN вимагає списку, розділеного комою, а не стовпця, що було просто неправильно. Ось запит, який працював на мене:SELECT users.name, users.uid, users.mail, from_unixtime(created) FROM users INNER JOIN ( SELECT mail FROM users GROUP BY mail HAVING count(mail) > 1 ) dup ON users.mail = dup.mail ORDER BY users.mail, users.created;
подвійний кінець

4
select * from table_name t1 inner join (select distinct <attribute list> from table_name as temp)t2 where t1.attribute_name = t2.attribute_name

Для вашого столу це було б щось на кшталт

select * from list l1 inner join (select distinct address from list as list2)l2 where l1.address=l2.address

Цей запит дасть вам усі різні записи адреси у таблиці списку ... Я не впевнений, як це буде працювати, якщо у вас є якісь значення первинного ключа для імені тощо.


4

Найшвидша процедура запитів на видалення дублікатів:

/* create temp table with one primary column id */
INSERT INTO temp(id) SELECT MIN(id) FROM list GROUP BY (isbn) HAVING COUNT(*)>1;
DELETE FROM list WHERE id IN (SELECT id FROM temp);
DELETE FROM temp;

2
Це очевидно видаляє лише перший запис з кожної групи дублікатів.
Palec

4

Особисто цей запит вирішив мою проблему:

SELECT `SUB_ID`, COUNT(SRV_KW_ID) as subscriptions FROM `SUB_SUBSCR` group by SUB_ID, SRV_KW_ID HAVING subscriptions > 1;

Цей скрипт - це відображення всіх таблиць ідентифікаторів абонента, які існують не один раз, у таблиці та кількості знайдених дублікатів.

Це стовпці таблиці:

| SUB_SUBSCR_ID | int(11)     | NO   | PRI | NULL    | auto_increment |
| MSI_ALIAS     | varchar(64) | YES  | UNI | NULL    |                |
| SUB_ID        | int(11)     | NO   | MUL | NULL    |                |    
| SRV_KW_ID     | int(11)     | NO   | MUL | NULL    |                |

Сподіваємось, це буде корисно і для вас!


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

Замініть місто своїм столом. Замініть ім'я на ім'я поля



0
    Find duplicate Records:

    Suppose we have table : Student 
    student_id int
    student_name varchar
    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

    Now we want to see duplicate records
    Use this query:


   select student_name,student_id ,count(*) c from student group by student_id,student_name having c>1;

+--------------------+------------+---+
| student_name        | student_id | c |
+---------------------+------------+---+
| usman               |        101 | 3 |
| muhammadusmanyaqoob |        103 | 2 |
+---------------------+------------+---+

0

Для швидкого перегляду повторюваних рядків ви можете запустити один простий запит

Ось я запитую таблицю і перелічу всі повторювані рядки з тим самим user_id, market_place та sku:

select user_id, market_place,sku, count(id)as totals from sku_analytics group by user_id, market_place,sku having count(id)>1;

Щоб видалити повторюваний рядок, ви повинні вирішити, який рядок потрібно видалити. Наприклад, той, хто має нижчий ідентифікатор (як правило, старший) або, можливо, іншу інформацію про дату. У моєму випадку я просто хочу видалити нижній ідентифікатор, оскільки новіший ідентифікатор - це остання інформація.

Спочатку перевірте, чи будуть видалені правильні записи. Тут я вибираю запис серед дублікатів, які будуть видалені (за унікальним ідентифікатором).

select a.user_id, a.market_place,a.sku from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

Потім я запускаю запит на видалення, щоб видалити дупи:

delete a from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

Резервне копіювання, подвійна перевірка, перевірка, перевірка резервної копії та виконання.


-1

select address from list where address = any (select address from (select address, count(id) cnt from list group by address having cnt > 1 ) as t1) order by address

внутрішній підзапит запиту повертає рядки з подвійною адресою, тоді зовнішній підзапит повертає стовпчик адреси для адреси з дублікатами. зовнішній підзапит повинен повертати лише один стовпець, оскільки він використовується як операнд для оператора "= будь-який"


-1

Відповідь Powerlord справді найкраща, і я рекомендую ще одну зміну: використовуйте LIMIT, щоб db не перевантажувався:

SELECT firstname, lastname, list.address FROM list
INNER JOIN (SELECT address FROM list
GROUP BY address HAVING count(id) > 1) dup ON list.address = dup.address
LIMIT 10

Корисно використовувати LIMIT, якщо немає де і коли приєднується. Почніть з невеликого значення, перевірте, наскільки важкий запит, а потім збільште ліміт.


як це щось сприяє чомусь?
Кеннет Селесте
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.