在MySQL中寻找重复值


769

我有一个带有varchar列的表,我想在此列中查找具有重复值的所有记录。我可以用来查找重复项的最佳查询是什么?


1
由于您提到查找所有记录,因此我假设您需要了解该varchar列中的键和重复的值。
TechTravelThink

在获得值之后,我可以轻松找到键,我真的只想要所有重复值的列表。
乔恩·塔卡伯里

Answers:


1521

SELECTGROUP BY子句做一个。假设name是您要在其中查找重复项的列:

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

这将返回在第一列中具有名称值的结果,并计算该值在第二列中出现的次数。


27
但是,如果您无法获取具有重复值的行的ID,这有什么用呢?是的,您可以为每个重复值进行新的查询匹配,但是是否可以简单列出重复项?
NobleUplift 2014年

23
@NobleUplift您可以执行a GROUP_CONCAT(id),它将列出ID。请参阅我的答案作为示例。
马特·拉顿

5
如果说那是什么意思ERROR: column "c" does not exist LINE 1
用户

15
我很困惑为什么这是公认的答案,为什么它会有这么多的反对意见。OP询问:“我想在此列中找到所有具有重复值的记录。” 该答案返回一个计数表。-1
Monica Heddneck

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 2014年

8
这应该是可接受的答案,因为GROUP BYHAVING仅返回可能的重复项之一。此外,使用索引字段代替的性能也COUNT(*)可以ORDER BY将重复的记录分组。
雷米·布雷顿

1
如上面的注释所述,此查询使您可以列出所有重复的行。很有用。
TryHarder

4
看着这个,我根本不明白它是如何工作的。内部条件不会一直为真,因为外部表中的任何行也将在内部表中可用,因此每一行至少总是匹配自己吗?我尝试查询,并得到了我怀疑的结果-返回的每一行。但是有这么多的反对我怀疑自己。内部查询是否不丢失“ AND mto.id <> mti.id”之类的内容?当我添加它时,它确实对我有用。
Clox

2
@Quassnoi好吧。我尝试将其放在sqlfiddle上,但是由于我尝试运行的每个查询(除了创建架构之外)都已超时,因此我已经放弃了。我确实发现,仅删除“ EXISTS”也可以使查询对我来说正常工作。
Clox

144

根据levik的答案来获取重复行的ID,GROUP_CONCAT如果服务器支持,则可以执行以下操作(这将返回逗号分隔的ID列表)。

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

12
所有这些时间都不知道GROUP_CONCAT()!非常非常有用。
艾塞德(Aesede)2015年

非常感谢Matt。这确实有帮助!对于那些试图在phpmyadmin中进行更新的人,如果您将id留给这样的函数:SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]它启用内联编辑,并且应更新所有涉及的行(或至少匹配的第一行),但是不幸的是,该编辑会产生Javascript错误。 ..
Armfoot

然后,您将如何计算要复制的ID数?
CMCDragonkai

2
我如何不将所有ID分组,而是从头到尾列出?在它们旁边的列中有它们各自的值?因此,它没有显示分组,而是显示了ID 1及其值,ID 2及其值。即使ID的值相同也是如此。
MailBlade

1
极有帮助的答案,这应该是首要的,以便更多的人看到它。我记得我在创建这样的列表时经历了多少痛苦,而且一直可以作为命令使用。.–
约翰·

13

假设您的表名为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

这种方法相对于以上答案的优势在于它提供了密钥。


4
+1,因为它很方便。尽管具有讽刺意味的是,结果本身包含重复项(它列出了a和b,然后列出了b和a。)
Fabien Snauwaert,2016年

2
@FabienSnauwaert您可以通过比较小于(或大于)来摆脱某些重复项
迈克尔·麦克尔

@TechTravelThink您的答案非常清楚,谢谢您,但是在较大的表上需要一些时间(在更多的20'000个条目表上大约需要200万),并且在显示25个第一个结果后,如果我单击以显示下一个结果,则phpmyadmin显示错误“#
1052-

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

1
不,因为这很可能是最慢的。众所周知,子选择的速度很慢,因为它们对返回的每一行都执行。
奥德曼

10

要查找Employee的name列中有多少条记录重复,以下查询将很有帮助;

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 =您要查找的重复数据。


此图显示了自己行中的每个重复项。那就是我所需要的。谢谢。
warmwhisky

8

我的最终查询在此处合并了一些有用的答案-组合group by,count和GROUP_CONCAT。

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

这提供了两个示例的ID(用逗号分隔),我需要的条形码以及重复的数量。

相应地更改表和列。


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
仅供参考-如果可能存在多个重复记录,则需要“选择不同的somecol ..”,否则结果将包含找到的重复行的重复。
提请

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工作台

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 2014年


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.emaila.*并获取重复的行的所有ID。
NobleUplift 2014年

@NobleUplift您在说什么?
迈克尔(Michael)

@Michael Well,因为这已经三岁了,所以我无法在所使用的任何版本的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提供的行包含匹配的(即重复的)交易,但第二次匹配相同的交易对时,它也提供了相反的交易ID,因此外部SELECT可以按第一个交易ID进行分组通过使用LEASTGREATEST确保结果中两个transactionid的顺序始终相同,这使得GROUP第一个交易ID变得安全,从而消除了所有重复的匹配项。在不到2秒的时间内浏览了近一百万条记录,识别出12,000多次比赛。当然,transactionid是主要索引,这确实有所帮助。





0

尝试使用以下查询:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.