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

これをどのように行うことができるかについての考えはありますか?最初のコードを実行してから、コード内の2番目のクエリで重複を調べることを回避しようとしています。

回答:


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

11
これは無相関のサブクエリなので、どちらかのクエリだけが適切に設計されていないと想定しても、それほど悪くないはずです。
ʞɔıu

素敵です。これは「エラー1248(42000):すべての派生テーブルには独自のエイリアスが必要」
に関する構文だと思います

3
これは正しい考えですが、以下のように、これはアドレスが標準化されていることが保証されている場合にのみ機能します...
Matt

30
このクエリで+1
すると

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

5
これは、Laravelで使用する最も簡単なクエリです。->having(DB::raw('count(*)'), '>', 2)クエリに追加する必要がありました。どうもありがとう!
コバ

1
1000万行のテーブルでうまく機能します。これが最良の答えでなければなりません
テリー林

13
この回答には注意してください。重複の1つだけを返します。同じレコードのコピーが2つ以上ある場合、それらはすべて表示されず、返されたレコードを削除した後も、テーブルに重複があります。
ミキコジェーン

7
なんで>=2?使用するだけHAVING COUNT(*) > 1
BadHorsie

2
@TerryLinこれが最初に述べられた問題(すべての重複を返す方法でした)を実際に解決しないことを考えると、私は同意しません。
マイケル

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

アドレスが3つ以上存在する可能性がある場合は、DISTINCTが必要です。


20
私もこれをテストしましたが、私の状況で受け入れられているソリューション(最新のMySQL、120.000行のテーブル)と比較して、約6倍遅くなりました。これは一時テーブルが必要なためである可能性があります。両方でEXPLAINを実行して違いを確認してください。

4
クエリの最後の部分を変更して、WHERE a.id > b.id新しい重複のみを除外するように変更DELETEしました。これにより、結果に対して直接実行できます。比較を切り替えて、古い重複をリストします。
Stoffe 2013

1
これを実行するには50秒かかり、@ doublejoshの応答には0.13秒かかりました。
antonagestam 2014年

1つの住所が3倍になり、出力行が2倍になるため、WHEREにもかかわらず、この回答は重複した回答になることを付け加えておきます。4倍だと3倍になると思います。
Wli

私はこれをリートコード「leetcode.com/problems/duplicate-emails」でテストしました。サブクエリに比べて高速でした。
2016年

56

私はこの質問に選ばれた最良の答えを試しましたが、それは私を幾分混乱させました。実際には、テーブルの1つのフィールドだけでそれが必要でした。このリンクからの次の例は、私にとって非常にうまくいきました。

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
600 000行すべてをロードするよりもはるかに速く、一意にするために〜10 000の重複行を処理する必要があった私のために働きました。
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働いています。SmartyStreetsでは、検証や重複除外などの問題に取り組んでおり、同様の問題を抱えた多くの多様な課題を見てきました。

リストの重複にフラグを付けるサードパーティのサービスがいくつかあります。これをMySQLサブクエリのみで実行しても、アドレス形式と標準の違いは考慮されません。USPS(米国住所用)には、これらの標準を作成するための特定のガイドラインがありますが、そのような操作を実行することが認定されているベンダーはほんの一握りです。

したがって、たとえば、テーブルをCSVファイルにエクスポートして、対応するリストプロセッサに送信するのが最善の方法です。SmartyStreetsの一括アドレス検証ツールは、数秒から数分で自動的に実行されます。「重複」と呼ばれる新しいフィールドとその中の値で重複行にフラグを立て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

この場合に実際に行っているのは、元のリストテーブルを取得し、そこから2つのp retendテーブル(p 1p 2)を作成して、住所列(3行目)で結合を実行することだけです。4行目は、同じレコードが結果セットに複数回表示されないようにします(「重複した重複」)。


1
うまくいきます。WHEREがLIKEでチェックしている場合、アポストロフィも検索されます。クエリが遅くなりますが、私の場合はワンタイマーです。
gossi

10

あまり効率的ではありませんが、うまくいくはずです。

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

10

これにより、1つのテーブルパスで重複が選択され、サブクエリは選択されません。

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
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のすべてのニーズは、サブクエリが単一の列を返す必要があることです。とてもシンプルです。インデックス付けされていない列でサブクエリが生成されている可能性が高いため、実行に非常に長い時間がかかります。2つのクエリに分割するのに長い時間がかかるかどうかをお勧めします。サブクエリを取得し、それを最初に一時テーブルに実行し、それにインデックスを作成してから、一時テーブルの重複フィールドでサブクエリを実行する完全クエリを実行します。
Ryan Roper

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;
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、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つの列のみを返す必要があります


-1

パワーロードの答えは確かに最高で、もう1つの変更をお勧めします。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を使用することをお勧めします。小さい値から始め、クエリの負荷をチェックしてから、制限を増やします。


これはどのように何かに貢献していますか?
ケネットセレステ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.