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) : 파생 된 모든 테이블에는 고유 한 별칭이 있어야합니다"라는 구문이
맞습니다.

3
이것은 올바른 생각이지만, 아래에서와 같이 주소가 표준화되도록 보장 된 경우에만 작동합니다.
Matt

30
+1이 쿼리를 사용하면 중복을 찾을 수 있지만 중복, 4 중 반복 ..... 등
albanx

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

5
이것은 Laravel과 함께 사용하기 가장 쉬운 쿼리였습니다. ->having(DB::raw('count(*)'), '>', 2)쿼리 에 추가 해야했습니다. 많은 감사합니다!
Kovah

1
천만 행 테이블과 잘 작동합니다. 이것은 최선의 해답이 될한다
테리 린

13
이 답변에주의하십시오. 중복 중 하나만 반환합니다. 동일한 레코드의 사본이 두 개 이상인 경우 해당 레코드를 모두 볼 수 없으며 레코드를 삭제 한 후에도 여전히 테이블에 중복이 있습니다.
Mikiko Jane

7
>=2? 그냥 사용하십시오HAVING COUNT(*) > 1
BadHorsie

2
@TerryLin 이것이 실제로 언급 된 문제 (모든 복제본을 반환하는 방법)를 해결하지 못한다는 것을 고려하면 동의하지 않습니다.
Michael

198

INNER가 왜 테이블 자체에 참여하지 않습니까?

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. 이전 복제본을 나열하려면 비교를 전환하십시오.
Stoffe

1
@doublejosh의 답변은 .13 초가 걸렸습니다.
antonagestam

하나의 주소가 세 배이고 출력 행이 두 배가되는 경우 WHERE에도 불구 하고이 답변이 중복 답변을 제공한다고 덧붙여 야합니다. 4 배이면 응답이 3 배가 될 것입니다.
Wli

leetcode " leetcode.com/problems/duplicate-emails " 에서 이것을 테스트했습니다 . 하위 쿼리에 비해 빠릅니다.
billow

56

이 질문에 선택된 최선의 답변을 시도했지만 다소 혼란 스럽습니다. 나는 실제로 내 테이블의 단일 필드에서만 필요했습니다. 이 링크 의 다음 예제는 저에게 매우 효과적이었습니다.

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

매력처럼 작동합니다!
Vinícius

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
60000 행을 모두로드하는 것보다 훨씬 빠릅니다.
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

20

중복은 하나 이상의 필드에 의존한다는 것을 알 수 있습니다.이 경우 아래 형식을 사용할 수 있습니다.

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

16

중복 주소를 찾는 것은 특히 정확성이 필요한 경우보다 훨씬 복잡합니다. 이 경우 MySQL 쿼리로는 충분하지 않습니다 ...

SmartyStreets 에서 일하면서 유효성 검사 및 중복 제거 및 기타 문제를 해결하고 비슷한 문제로 다양한 과제를 겪었습니다.

목록에 중복 항목을 표시하는 여러 타사 서비스가 있습니다. MySQL 하위 쿼리로만이 작업을 수행한다고해서 주소 형식과 표준의 차이를 설명하지는 않습니다. USPS (미국 주소)에는 이러한 표준을 만들기위한 특정 지침이 있지만 소수의 공급 업체 만 이러한 작업을 수행하도록 인증되었습니다.

따라서 가장 좋은 대답은 예를 들어 테이블을 CSV 파일로 내보내고 가능한 목록 프로세서에 제출하는 것입니다. 그 중 하나가 SmartyStreets Bulk Address Validation Tool 이며 몇 초에서 몇 분 안에 자동으로 수행됩니다. 중복 행이라는 새 필드와 그 안에 값이 Y있는 중복 행에 플래그 를 지정합니다.


6
OP의 "중복 레코드"질문 자체가 복잡하지는 않지만 주소를 비교할 때도 지정할 수 있습니다. 주소 문자열 일치에 어려움이있는 경우 +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 1P 2를 그 밖으로, 다음은 주소 열 (3 호선)에 가입 수행 -. 네 번째 줄은 동일한 레코드가 결과 집합에 여러 번 표시되지 않도록합니다 ( "중복 복제본").


1
잘 작동합니다. WHERE가 LIKE로 확인하면 아포스트로피도 발견됩니다. 쿼리를 느리게하지만 제 경우에는 한 번입니다.
gossi

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

8

또한 복제본 수를 표시하고 조인없이 결과를 정렬합니다.

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

여전히 중복되는 항목 수를 말한다 때문에 완벽
데니스

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

이것도 시도했지만 그냥 매달린 것 같습니다. 내부 쿼리의 리턴이 IN 매개 변수 형식을 만족하지 않는다고 생각하십시오.
doublejosh

in 매개 변수 형식을 만족하지 않는 것은 무엇을 의미합니까? IN에 필요한 것은 하위 쿼리가 단일 열을 반환해야한다는 것입니다. 정말 간단합니다. 인덱싱되지 않은 열에서 하위 쿼리가 생성 될 가능성이 높으므로 실행하는 데 시간이 많이 걸립니다. 두 개의 쿼리로 나누는 데 시간이 오래 걸리는 것이 좋습니다. 하위 쿼리를 가져 와서 임시 테이블로 먼저 실행 한 다음 인덱스를 생성 한 다음 임시 테이블에서 중복 필드가있는 하위 쿼리를 수행하여 전체 쿼리를 실행하십시오.
Ryan Roper

나는 열이 아닌 쉼표로 구분 된 목록이 필요하다고 걱정했습니다. 나를 위해 일한 쿼리는 다음과 같습니다.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;
doublejosh

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;

이 스크립트는 테이블에 두 번 이상 존재하는 모든 가입자 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.