4

一意の行を持つ代替の一時的な MySQL テーブルを作成する解決策を見ましたが、テーブルが非常に大きく、それらを移動するのが面倒になるため、そのアイデアは気に入りませんでした (また、テーブルの移動中にエラーが発生すると大きな問題が発生します)。動き)。

しかし、私は次のことを見つけました。これについてどう思いますか (チェックする重複は「field_name」です)。

DELETE FROM table1
USING table1, table1 as vtable
WHERE (NOT table1.ID=vtable.ID)
AND (table1.field_name=vtable.field_name)

誰かがこれでうまくいくはずだと言っていましたが、よくわかりません。どう思いますか?また、「field_name」にインデックスを付けると、このコマンドのパフォーマンスがまったく変わりますか?

編集:クエリを実行する前にテストする方法はありますか? 私の知る限り、MySQL は DELETE クエリでの "explain" をサポートしていません。

4

3 に答える 3

4

表示するクエリは両方の重複を削除することに注意してください。どちらか一方を維持したいと思います。

このクエリの書き方は次のとおりです。

DELETE t1 FROM table1 AS t1 JOIN table1 AS t2 
  ON t1.id > t2.id AND t1.field_name = t2.field_name;

not-equals-to の代わりに greater-than を使用すると、両方ではなく、1 つの行 (後の行) のみを削除します。

(id, field_name) に対する複合インデックスが役立つ場合があります。EXPLAIN最適化レポートを取得するには、MySQL でこれを確認する必要があります。ただし、クエリEXPLAINのみをサポートしているSELECTため、同等のものを実行しSELECTて最適化を確認する必要があります。

EXPLAIN SELECT * FROM table1 AS t1 JOIN table1 AS t2 
  ON t1.id > t2.id AND t1.field_name = t2.field_name;

また、テストについても尋ねました。重複を含む行のサンプルをtestデータベースのテーブルにコピーすることをお勧めします。

CREATE TABLE test.table1test SELECT * FROM realdb.table1 LIMIT 10000;

DELETEこれで、解が正しいと確信できるまで、サンプル データに対して実験を実行できます。

USE test;
SET autocommit = 0;
DELETE ... 
ROLLBACK;

データベース内のスクラッチ テーブルにはtest、実際のデータベース内の実際のテーブルとは別の名前を付けることをお勧めします。DELETE誤って実際のデータベースをデフォルトのデータベースとして使用している間に実験を実行した場合に備えて!


あなたのコメントについて:

USE testmysql クライアントの組み込みコマンドです。testデータベースをデフォルトのデータベースとして設定します。データベース名で修飾せずにクエリでテーブルに名前を付けると、これがデフォルトのデータベースになります。http://dev.mysql.com/doc/refman/5.1/en/use.htmlを参照してください。

SET autocommit = 0各クエリのトランザクションを暗黙的にコミットするデフォルトの動作をオフにします。したがって、トランザクションを終了するには、 COMMITorコマンドを明示的に指定する必要があります。http://dev.mysql.com/doc/refman/5.1/en/commit.htmlROLLBACKを参照してください。

ROLLBACKそのトランザクションで行われた変更が破棄されるため、実験するときに使用する価値があります。これは、別の実験を試すことができるように、テスト データの初期状態にすばやく戻る方法です。

DELETE t1はタイプミスではありません。 DELETEテーブル全体ではなく行を削除します。 ステートメントの条件を満たす各行t1の別名です(ただし、テーブル内のすべての行が条件に含まれる可能性があります)。http://dev.mysql.com/doc/refman/5.1/en/delete.htmlで複数テーブルの削除の説明を参照してください。

PHP でループを実行し、変数を使用してループを反復処理するときのようなものです: for ($i=0; $i<100; ++$i)... 変数$iは一連の値を取り、ループを通過するたびに異なる値を持ちます。

これは、私のソリューションが複数の重複を削除する方法を示すデモです。これをtestデータベースで実行し、コマンド ウィンドウから直接結果を貼り付けています。

mysql> create table table1 (id serial primary key, field_name varchar(10));
Query OK, 0 rows affected (0.45 sec)

mysql> insert into table1 (field_name) 
       values (42), (42), (42), (42), (42), (42);
Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql> select * from table1;
+----+------------+
| id | field_name |
+----+------------+
|  1 | 42         | 
|  2 | 42         | 
|  3 | 42         | 
|  4 | 42         | 
|  5 | 42         | 
|  6 | 42         | 
+----+------------+
6 rows in set (0.00 sec)

mysql> delete t1 from table1 t1 join table1 t2 
       on t1.id > t2.id and t1.field_name = t2.field_name;
Query OK, 5 rows affected (0.00 sec)

mysql> select * from table1;
+----+------------+
| id | field_name |
+----+------------+
|  1 | 42         | 
+----+------------+
1 row in set (0.00 sec)
于 2010-07-21T19:22:11.660 に答える
0

そのクエリは機能するはずです。インデックスがあるとパフォーマンスが変わりますが、実際にはテーブルのサイズに依存します。

これをテストするために、データのサブセットを一時テーブルにコピーし、実際のテーブルで実行する前に一時テーブルでコマンドを実行します。

いつでもロールバックできるように、主要なバッチ ジョブを実行する前に必ずテーブルをバックアップしてください。

于 2010-07-21T19:20:37.587 に答える
0

私が使用する方法は、条件を回避し、JOIN大幅に高速化する必要があります。

DELETE FROM table1 WHERE id NOT IN (SELECT MIN(x.id) FROM table1 AS x GROUP BY x.field_name);

副選択は、保持したい ID のリストを収集します。これにより、それぞれに一意の行を保持できますfield_name。このDELETEステートメントは、余分な重複行をすべて削除します。

また、はい、field_nameフィールドのインデックスにより、クエリのパフォーマンスが向上します。

于 2010-07-21T19:21:48.820 に答える