在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

关于如何做到这一点的任何想法?我试图避免做第一个,然后在代码中用第二个查询查找重复项。

Answers:


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):每个派生表必须具有自己的别名”
周围的语法

3
这是正确的主意,但同样,如下所述,这仅在保证地址标准化的情况下才有效...
Matt

30
通过该查询+1,您可以找到重复项,也可以找到三重,四重.....等等
albanx 2012年

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

5
这是与Laravel一起使用的最简单的工作查询。只需添加->having(DB::raw('count(*)'), '>', 2)到查询中即可。非常感谢!
科瓦2015年

1
与1000万行表配合使用效果很好。这应该是最好的答案
Terry Lin

13
小心此答案。它仅返回重复项之一。如果同一记录有两个以上的副本,那么您将看不到它们的全部,删除返回的记录后,您的表中仍然会有重复项。
Mikiko Jane

7
为什么>=2呢 只需使用HAVING COUNT(*) > 1
BadHorsie

2
@TerryLin考虑到这实际上并不能解决最初说明的问题(即如何返回所有重复项),我不同意。
迈克尔(Michael)

198

为什么不将INNER JOIN本身与桌子相连?

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
我也对此进行了测试,与我所接受的解决方案(最新的MySQL,120.000行的表)相比,它的速度几乎慢了6倍。这可能是由于它需要一个临时表,因此对两者都运行EXPLAIN来查看差异。

4
我将查询的最后一部分更改为WHERE a.id > b.id仅过滤出较新的重复项,这样我就可以DELETE直接对结果进行处理。切换比较以列出较旧的重复项。
2013年

1
这花了50秒钟来运行,@ doublejosh的回答花了0.13秒钟。
antonagestam 2014年

我必须补充一点,尽管有一个WHERE,但这个答案仍给出了重复的答案,以防万一一个地址被加倍,输出行被加倍。如果是四倍,我相信回应将会是三倍。
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
为我工作,我只需要处理大约10000行重复的行,以使其唯一,比加载全部60万行要快得多。
adrianTNT

1
非常容易
-Shwet

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

20

我们发现重复项还取决于多个字段。对于这些情况,您可以使用以下格式。

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

16

查找重复的地址比看起来要复杂得多,尤其是在需要准确性的情况下。在这种情况下,MySQL查询是不够的...

我在SmartyStreets工作,我们致力于解决验证和重复数据删除等问题,并且我遇到了许多类似问题的各种挑战。

有一些第三方服务会为您标记列表中的重复项。仅使用MySQL子查询执行此操作不会解决地址格式和标准的差异。USPS(用于美国地址)具有制定这些标准的某些准则,但是只有少数供应商被认证可以执行此类操作。

因此,我建议您最好的答案是将表导出到CSV文件中,然后将其提交给有能力的列表处理器。SmartyStreets 批量地址验证工具就是其中之一,它将在几秒钟到几分钟内自动为您完成。它将使用称为“ Duplicate”的新字段及其中的值来标记重复的行Y


6
+1显示了匹配地址字符串的难度,尽管您可能希望指定OP的“重复记录”问题本身并不复杂,但是是在比较地址时发生的
故事

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 1p 2,然后在address列上执行(第3行)。第4行确保同一条记录不会在您的结果集中多次显示(“重复重复”)。


1
效果不错。如果WHERE正在检查LIKE,那么也会发现撇号。使查询变慢,但就我而言,它是一次性的。
gossi 2012年

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()OracleSQL Server

有关详细信息,请参见我的博客中的文章:


20
不是nitpick,而是FROM (SELECT ...) aoo子查询:-P
Rocket Hazmat 2012年

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

您不满足参数格式的意思是什么?所有IN需求是您的子查询必须返回单个列。这真的很简单。您的子查询更有可能是在没有索引的列上生成的,因此要花费大量的时间才能运行。我建议将它分为两​​个查询是否需要很长时间。以子查询为例,首先将其运行到临时表中,在其上创建索引,然后执行完整查询以执行子查询,其中子表位于临时表中。
Ryan Roper 2012年

我担心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 2015年

4

我个人这个查询解决了我的问题:

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

该脚本的作用是在表中显示不止一次存在的所有订户ID,并找到重复的数量。

这是表格列:

| 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

用表格替换城市。更换名称与字段名称


2
    SELECT *
    FROM (SELECT  address, COUNT(id) AS cnt
    FROM list
    GROUP BY address
    HAVING ( COUNT(id) > 1 ))

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;

要删除重复的行,您必须确定要删除的行。例如ID较低的人(通常是年龄较大的人)或其他一些日期信息。就我而言,我只想删除较低的ID,因为较新的ID是最新信息。

首先,请仔细检查是否删除了正确的记录。在这里,我从重复项中选择要删除的记录(通过唯一ID)。

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

内部子查询返回具有重复地址的行,然后外部子查询返回具有重复地址的地址列。外部子查询只能返回一列,因为它用作运算符'= any'的操作数


-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

如果没有WHERE和进行联接,则使用LIMIT是一个好习惯。从较小的值开始,检查查询的强度,然后增加限制。


这对任何事情都有什么贡献?
Kennet Celeste
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.