Пошук повторюваних значень у MySQL


769

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


1
Оскільки ви згадали, знайдете всі записи, я припускаю, що вам потрібно знати KEYS, а також скопійовані значення в цьому стовпчику varchar.
TechTravelThink

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

Відповіді:


1521

Зробіть SELECTз GROUP BYпунктом. Скажімо, назва - це стовпець, у якому потрібно знайти дублікати:

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

Це поверне результат зі значенням імені у першому стовпчику та підрахунком, скільки разів це значення з’явиться у другому.


27
Але як це корисно, якщо ви не можете отримати ідентифікатори рядків з повторюваними значеннями? Так, ви можете виконати нове відповідність запиту для кожного дублюючого значення, але чи можна просто перелічити дублікати?
NobleUplift

23
@NobleUplift Ви можете зробити це, GROUP_CONCAT(id)і він перелічить ідентифікатори. Дивіться мою відповідь для прикладу.
Метт Рардон,

5
Що це означало б, якби це сказало ERROR: column "c" does not exist LINE 1?
Користувач

15
Мене бентежить, чому це прийнята відповідь і чому в ній так багато відгуків. ОП запитала: "Я хотів би знайти в цьому стовпці всі записи, які мають повторювані значення". Ця відповідь повертає таблицю підрахунків. -1
Моніка Хеднек

4
Для тих, хто не розуміє, як працює HAVING - це просто фільтр на набір результатів, так це відбувається після основного запиту.
Джон Хант

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

10
Прекрасна відповідь @ levik, оскільки вона не додає зайвого стовпця. Це робить його корисним для використання з 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
        )

Цей запит повертає повні записи, а не лише окремі varchar_column .

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

varchar_columnЗвичайно, наявність індексу на швидкість прискорить цей запит.


3
Дуже добре. Я додав ORDER BY varchar_column DESCдо кінця запиту.
trante

8
Це повинно бути прийнято відповідати, так як GROUP BYі HAVINGповертає тільки один з можливих дублікатів. Також продуктивність із індексованим полем COUNT(*), а не можливість ORDER BYгрупування дублікатів записів.
Ремі Бретон

1
Як зазначено в коментарях вище, цей запит дозволяє перелічити всі повторювані рядки. Дуже корисний.
TryHarder

4
Дивлячись на це, я не розумію, як би це взагалі працювало. Невже внутрішній стан завжди відповідає дійсності, оскільки будь-який рядок у зовнішній таблиці також буде доступний у внутрішній таблиці, і тому кожен рядок завжди буде, як мінімум, відповідає собі? Я спробував запит і отримав результат, за яким я підозрював - кожен ряд повертався. Але з такою кількістю результатів я сумніваюся в собі. Чи не міститься внутрішнього запиту щось на зразок "AND mto.id <> mti.id"? Це працює для мене, коли я додаю це.
Clox

2
@Quassnoi Добре. Я намагався поставити його на sqlfiddle, але я відмовився, оскільки кожен запит, який я намагаюся запустити, окрім створення схеми вичерпується. Я зрозумів, що видалення "EXISTS" також робить запит правильно для мене.
Clox

144

Побудова відповіді levik, щоб отримати ідентифікатори дублікатів рядків, ви можете зробити це, GROUP_CONCATякщо ваш сервер підтримує це (це поверне розділений комою список ідентифікаторів).

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

12
Весь цей час, не знаючи про GROUP_CONCAT ()! дуже дуже корисно.
асеза

Дуже цінується Метта. Це справді корисно! Для тих, хто намагається оновити у phpmyadmin, якщо ви залишаєте ідентифікатор разом із такою функцією: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]він дозволяє вбудовувати редагування, і він повинен оновлювати всі задіяні рядки (або принаймні першу відповідність), але, на жаль, редагування створює помилку Javascript. ..
Armfoot

Як би ви потім обчислили, скільки ідентифікаторів підлягають дублюванню?
CMCDragonkai

2
Як я не згрупую всі ідентифікатори, але натомість перераховані від першого до останнього; з усіма відповідними значеннями у стовпцях поруч? Отже, замість того, щоб групувати його, він просто показує ID 1 та його значення, ID 2 та його значення. ВСЕ, якщо значення для ідентичного номера однакові.
MailBlade

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

13

Припустимо, що ваша таблиця називається TableABC, а стовпець, який ви хочете, - Col, а основний ключ для T1 - ключ.

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

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


4
+1 Тому що це зручно. Хоча, за іронією долі, сам результат містить дублікати (у ньому перераховані а і b, потім b і a)
Фаб'єн Сноуаерт

2
@FabienSnauwaert Ви можете позбутися деяких дублікатів, порівнявши менше (або більше)
Michael

@TechTravelThink ваша відповідь дуже чітка, спасибі за це, але на великій таблиці потрібен певний час (приблизно 2 млн. На більше 20'000 таблиць записів), і після показу перших результатів 25, якщо я натискаю, щоб показати наступний, phpmyadmin показати помилку "# 1052 - стовпець "id" у порядку порядку неоднозначний "
bcag2

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

1
Ні, тому що це цілком можливо найповільніший з партії. Підселектори є, як відомо, повільними, оскільки вони виконуються для кожного повернутого рядка.
Оддман

10

Щоб дізнатись, скільки записів є дублікатами у стовпці імен у співробітнику, корисний наступний запит;

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

10

щоб отримати всі дані, що містять дублювання, я використав це:

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 = таблиця, з якою ви працюєте.

DupliactedData = дублюються дані, які ви шукаєте.


Цей показує кожен дублікат у власному рядку. Це те, що мені потрібно. Дякую.
warwhisky

8

Мій остаточний запит містив декілька відповідей, які допомогли - об’єднати групу, підрахунок та GROUP_CONCAT.

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

Це дає ідентифікатор обох прикладів (відокремлений комами), потрібний мені штрих-код та кількість дублікатів.

Змініть таблицю та стовпці відповідно.


8

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

Такий підхід дає фактичні подвоєні результати.

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 - Ви хочете "вибрати окремий somecol ..", якщо існує можливість існувати більше ніж 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

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


7

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

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

6

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

Але якщо вам потрібно перевірити більше стовпців і хочете перевірити комбінацію результату, щоб цей запит спрацював нормально:

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 поля для дублікатів: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

Я вважаю за краще використовувати віконні функції (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


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
Виконання одного і того ж запиту двічі здається неефективним.
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
Варто зазначити, що це нестерпно повільно або може навіть не закінчитися, якщо стовпець, на який запитують, не індексується. В іншому випадку, я був в змозі змінити , a.emailщоб a.*і отримати всі ідентифікатори рядків з дублікатами.
NobleUplift

@NobleUplift Про що ти говориш?
Майкл

@Michael Добре, оскільки це три роки, я не можу перевірити, яку версію MySQL я використовував, але я спробував цей самий запит у базі даних, де вибраний стовпець не мав на ньому індексу, тому це зайняло досить кілька секунд, щоб закінчити. Змінивши його на SELECT DISTINCT a.*вирішене майже миттєво.
NobleUplift

@NobleUplift Ну добре. Я можу зрозуміти, що це повільно ... частина, яка мене турбує, - це "може навіть не закінчити".
Майкл

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

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;

ви можете також додати пояснення?
Роберт

Чому б не використовувати CREATE TEMPORARY TABLE ...? Трохи пояснення вашого рішення було б чудово.
maxhb

1

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

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 секунд. Звичайно трансакціонід - це первинний показник, який справді допоміг.




1

Якщо ви хочете видалити дублікат, використовуйте DISTINCT

В іншому випадку використовуйте цей запит:

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


0

Спробуйте скористатися цим запитом:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.