MySQLテーブルの重複を削除することは一般的な問題です。これは、一般に、事前に制約が欠落しているために重複を回避した結果です。しかし、この一般的な問題には通常、特定のニーズが伴います。特定のアプローチが必要です。アプローチは、たとえば、データのサイズ、保持する必要がある重複したエントリ(通常、最初または最後のエントリ)、保持するインデックスがあるかどうか、または追加の処理を実行するかどうかによって異なります。複製されたデータに対するアクション。
MySQL自体にもいくつかの特殊性があります。たとえば、テーブルのUPDATEを実行すると、FROM原因で同じテーブルを参照できないなどです(MySQLエラー#1093が発生します)。この制限は、一時テーブルで内部クエリを使用することで克服できます(上記のいくつかのアプローチで提案されています)。ただし、この内部クエリは、ビッグデータソースを処理する場合は特にうまく機能しません。
ただし、重複を削除するためのより良いアプローチが存在します。これは効率的で信頼性が高く、さまざまなニーズに簡単に適応できます。
一般的なアイデアは、新しい一時テーブルを作成し、通常は重複を回避するために一意の制約を追加し、重複を処理しながら、以前のテーブルから新しいテーブルにデータを挿入することです。このアプローチは、単純なMySQL INSERTクエリに依存し、それ以上の重複を避けるために新しい制約を作成し、重複を検索するために内部クエリを使用する必要性と、メモリに保持する必要がある一時テーブルをスキップします(したがって、大きなデータソースにも適合します)。
これは、それを実現する方法です。次の列を持つテーブルemployeeがあるとします。
employee (id, first_name, last_name, start_date, ssn)
重複するssn列を持つ行を削除し、最初に見つかったエントリのみを保持するには、次のプロセスに従います。
-- create a new tmp_eployee table
CREATE TABLE tmp_employee LIKE employee;
-- add a unique constraint
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
-- scan over the employee table to insert employee entries
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id;
-- rename tables
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
技術説明
- ライン#1は、新たな作成tmp_eployeeのと全く同じ構造のテーブルを従業員テーブル
- 2行目では、新しいtmp_eployeeテーブルにUNIQUE制約を追加して、重複を回避しています。
- 行#3は、IDで元の従業員テーブルをスキャンし、重複したエントリを無視しながら、新しい従業員エントリを新しいtmp_eployeeテーブルに挿入します。
- 行#4はテーブルの名前を変更するため、新しい従業員テーブルは重複なしですべてのエントリを保持し、以前のデータのバックアップコピーはbackup_employeeテーブルに保持されます。
⇒ このアプローチを使用して、1.6Mレジスタは200秒未満で6kに変換されました。
Chetan、このプロセスに従って、次のコマンドを実行することにより、すべての重複をすばやく簡単に削除し、UNIQUE制約を作成できます。
CREATE TABLE tmp_jobs LIKE jobs;
ALTER TABLE tmp_jobs ADD UNIQUE(site_id, title, company);
INSERT IGNORE INTO tmp_jobs SELECT * FROM jobs ORDER BY id;
RENAME TABLE jobs TO backup_jobs, tmp_jobs TO jobs;
もちろん、このプロセスをさらに変更して、重複を削除するときのさまざまなニーズに適応させることができます。次にいくつかの例を示します。
the最初のエントリではなく最後のエントリを保持するためのバリエーション
最初のエントリではなく、最後に複製されたエントリを保持する必要がある場合があります。
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id DESC;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- 3行目では、ORDER BY id DESC句が最後のIDを残りのIDよりも優先させます。
found重複に対していくつかのタスクを実行するためのバリエーション、たとえば、見つかった重複をカウントする
場合によっては、見つかった重複エントリに対してさらに処理を実行する必要があります(重複のカウントを保持するなど)。
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- 行#3で、新しい列n_duplicatesが作成されます
- 4行目では、INSERT INTO ... ON DUPLICATE KEY UPDATEクエリを使用して、重複が見つかったときに追加の更新を実行します(この場合、カウンターを増やします)。INSERTINTO ... ON DUPLICATE KEY UPDATEクエリは次のようになります。見つかった重複に対して異なるタイプの更新を実行するために使用されます。
auto自動増分フィールドIDを再生成するためのバリエーション
自動インクリメンタルフィールドを使用する場合があります。インデックスをできるだけコンパクトに保つために、重複の削除を利用して、新しい一時テーブルで自動インクリメンタルフィールドを再生成できます。
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT (first_name, last_name, start_date, ssn) FROM employee ORDER BY id;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- 3行目では、テーブルのすべてのフィールドを選択する代わりに、IDフィールドがスキップされるため、DBエンジンは新しいフィールドを自動的に生成します。
✔その他のバリエーション
必要な動作に応じて、さらに多くの変更を行うこともできます。例として、次のクエリは、2番目の一時テーブルを使用して、1)最初のエントリではなく最後のエントリを保持します。2)見つかった重複のカウンターを増やす。また、3)以前のデータの場合と同じように入力順序を維持しながら、自動増分フィールドIDを再生成します。
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id DESC ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
CREATE TABLE tmp_employee2 LIKE tmp_employee;
INSERT INTO tmp_employee2 SELECT (first_name, last_name, start_date, ssn) FROM tmp_employee ORDER BY id;
DROP TABLE tmp_employee;
RENAME TABLE employee TO backup_employee, tmp_employee2 TO employee;