1ではなく2列で重複を見つける方法


107

興味のある2つの列を持つMySQLデータベーステーブルがあります。個別にそれらはそれぞれ重複を持つことができますが、同じ値を持つそれらの両方の重複を持つことはありません。

stone_idupshargeタイトルが異なる限り、逆も同様です。しかし、たとえばstone_id= 412とupcharge_title= "sapphire"の場合、組み合わせは1回だけ発生する必要があると言います。

これはOKです:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "ruby"

これは大丈夫ではありません:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "sapphire"

両方のフィールドで重複を見つけるクエリはありますか?そして、可能であれば、それを許可しないようにデータベースを設定する方法はありますか?

MySQLバージョン4.1.22を使用しています

回答:


192

2つのフィールドの間に複合キーを設定する必要があります。これには、各行に一意のstone_idとupcharge_titleが必要です。

既存の重複を見つける限り、これを試してください:

select   stone_id,
         upcharge_title,
         count(*)
from     your_table
group by stone_id,
         upcharge_title
having   count(*) > 1

ありがとう、それはそれらを選択します。重複を削除する方法を教えてもらえますか?
JD Isaacks 2009年

2
1つの方法は、すべての個別のデータを取得してテーブルを再作成することです。
宮城コーダー

1
@John Isaacks:区別できる他のフィールドがない場合(つまり、すべてのフィールドが重複している場合)、両方の行を削除して1つを再作成する必要があります。1つの方法は、重複をテーブルのコピーにコピーし、元のコピーから削除し、コピーから個別の行を再挿入することです。
Pダディ

これはpostgres 8.1では機能しません、誰かが私に手伝ってもらえますか?
レノン

ありがとうございます。あなたがグループ化した順序は問題ごとですか?
Andrew

35

"ALTER IGNORE"を使用して重複しないインデックスを追加すると、重複を削除し、あなたがやりたいように聞こえる一意のレコードを強制するのに役立ちました。したがって、構文は次のようになります。

ALTER IGNORE TABLE `table` ADD UNIQUE INDEX(`id`, `another_id`, `one_more_id`);

これにより、一意の制約が効果的に追加され、重複するレコードがなくなることを意味し、IGNOREは既存の重複を削除します。

ここでALTER IGNOREの詳細を読むことができます:http : //mediakey.dk/~cc/mysql-remove-duplicate-entries/

更新:@Inquisitiveから、MySql> 5.5のバージョンではこれが失敗する可能性があることが通知されました:

MySQL> 5.5、InnoDBテーブル、およびPerconaでは、InnoDB高速インデックス作成機能[ http://bugs.mysql.com/bug.php?id=40344]が原因で失敗します。この場合、最初に実行set session old_alter_table=1してから、上記のコマンドが正常に動作します

更新ALTER IGNORE-5.7で削除

ドキュメントから

MySQL 5.6.17以降、IGNORE句は非推奨になり、使用すると警告が生成されます。IGNOREはMySQL 5.7で削除されました。

MySQL開発者の1つは2つの選択肢を提供します。

  • 一意のフィールドでグループ化し、上記のように削除します
  • 新しいテーブルを作成し、一意のインデックスを追加し、を使用しますINSERT IGNORE。例:
CREATE TABLE duplicate_row_table LIKE regular_row_table;
ALTER TABLE duplicate_row_table ADD UNIQUE INDEX (id, another_id);
INSERT IGNORE INTO duplicate_row_table SELECT * FROM regular_row_table;
DROP TABLE regular_row_table;
RENAME TABLE duplicate_row_table TO regular_row_table;

しかし、テーブルのサイズによっては、これは実用的でない場合があります


1
本当ですが、少なくとも次回は知っています。同じ問題があり、他の人と共有するのは良いことだと思いました
SeanDowney

私はそれが3年遅れであることをいじめていました。本当にありがとうございます。したがって、プラス1.
JD Isaacks

これにより重複の1つが任意に削除されると思いますので、各行の間で、知っておく、または保持しておくと役立つデータに違いがないことを確認してください。
Joshua Pinter、

2年遅れても回答の+1。誤って複合キーを削除しましたが、これは命の恩人でした。ありがとう
ivcode

私はいくつかの重複検索テクニックを試しましたが、どれもこれほど単純で高速ではありませんでした。この方法を共有していただきありがとうございます。
クリスチャンO.

8

このような重複を見つけることができます。

Select
    stone_id, upcharge_title, count(*)
from 
    particulartable
group by 
    stone_id, upcharge_title
having 
    count(*) > 1

4

重複を見つけるには:

select stone_id, upcharge_title from tablename group by stone_id, upcharge_title having count(*)>1

今後これを回避するように制約するには、これら2つのフィールドに複合一意キーを作成します。


1
どうもありがとうございました。重複するものを1つだけ残して他をすべて削除する方法を教えてください。また、phpmyadminでcompisiteキーを設定するにはどうすればよいですか。ありがとうございました!!!
JD Isaacks 2009年

3

ちなみに、テーブルの複合一意制約は、最初にこれが発生するのを防ぎます。

ALTER TABLE table
    ADD UNIQUE(stone_id, charge_title)

(これは有効なT-SQLです。MySQLについては不明です。)


1
私はそれはうまくいくと思いますが、最初に重複を削除するまでそれをすることはできません。ありがとう。
JD Isaacks 2009年

1

このSOの投稿は私を助けましたが、私も行の1つを削除して保持する方法を知りたいと思っていました...重複した行を削除して1つを保持するPHPソリューションがあります(私の場合は2列しかなく、それは重複するカテゴリの関連付けをクリアする関数)

$dupes = $db->query('select *, count(*) as NUM_DUPES from PRODUCT_CATEGORY_PRODUCT group by fkPRODUCT_CATEGORY_ID, fkPRODUCT_ID having count(*) > 1');
if (!is_array($dupes))
    return true;
foreach ($dupes as $dupe) {
    $db->query('delete from PRODUCT_CATEGORY_PRODUCT where fkPRODUCT_ID = ' . $dupe['fkPRODUCT_ID'] . ' and fkPRODUCT_CATEGORY_ID = ' . $dupe['fkPRODUCT_CATEGORY_ID'] . ' limit ' . ($dupe['NUM_DUPES'] - 1);
}

(制限NUM_DUPES-1)は、単一の行を保持するものです...

皆さんありがとう


3
ALTER IGNORE TABLE table ADD UNIQUE INDEX index_name(stone_id, charge_title)重複する行を削除して、一意のペアを1つだけ残します。
dev-null-
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.