MySQLの重複を取り除からデータベースの迅速
-
22-07-2019 - |
質問
かん(>百万rows)MySQLデータベースにすぐにより重複している。されてはいかがでしょうか1/4から1/2をdbふれかえっています。かくして迅速にご注意いただきたいクエリの実行時).こちらはどう見えます:
id(インデックス)|text1|text2|text3
text1&text2組み合わせは、
がある場合を複製、組み合わせとtext3NULLでない場合ます。例:
1 | abc | def | NULL
2 | abc | def | ghi
3 | abc | def | jkl
4 | aaa | bbb | NULL
5 | aaa | bbb | NULL
...り:
1 | abc | def | ghi #(doesn't realy matter id:2 or id:3 survives)
2 | aaa | bbb | NULL #(if there's no NOT NULL text3, NULL will do)
新規id冷すると、何も普遍的な振る舞いが見られ、古いテーブルidを持っています。
ったようなもの:
CREATE TABLE tmp SELECT text1, text2, text3
FROM my_tbl;
GROUP BY text1, text2;
DROP TABLE my_tbl;
ALTER TABLE tmp RENAME TO my_tbl;
又は異なるその他のバリエーションがあります。
を作っていくという小さなデータベース、クエリの実行時間の鉱山は大きくはないので、実;>20分)
が速い。力を貸してください必要がありません。
解決
重複キー+ ifnull()を使用して、これでうまくいくと思います:
create table tmp like yourtable;
alter table tmp add unique (text1, text2);
insert into tmp select * from yourtable
on duplicate key update text3=ifnull(text3, values(text3));
rename table yourtable to deleteme, tmp to yourtable;
drop table deleteme;
group by、distinct、またはsubquery、さらにはorder byを必要とするものよりもはるかに高速である必要があります。これには、ファイルの並べ替えさえ必要ありません。これは、大きな一時テーブルのパフォーマンスを低下させます。元のテーブルを完全にスキャンする必要がありますが、それを避けることはできません。
他のヒント
必要なことを正確に行うために、この単純な1行のコードを見つけました。
ALTER IGNORE TABLE dupTest ADD UNIQUE INDEX(a,b);
DELETE FROM dups
WHERE id NOT IN(
SELECT id FROM (
SELECT DISTINCT id, text1, text2
FROM dups
GROUP BY text1, text2
ORDER BY text3 DESC
) as tmp
)
このクエリすべての記録、グループによる判別分野の受注IDしていることになり最初のnullでない場合text3す。それを選択し、その結果これらはid...れぞれを削除)すべて削除Idないます。
他のクエリをこのような影響を及ぼす表全体にはログインが必要です。だけで走を及ぼす場合がありますので行くのを防ぐことができます。
後はこれを"固定"私の適用UNIQUEインデックス(text1,text2するものとします。防止の仕の重複す。
行きたい場合は、"新しいテーブルに置き換えて古いもの"。を使用できるものを内のselectステートを挿入します。
MySQLの特定は新しいテーブル名my_tbl2、とまったく同じ構造:
INSERT INTO my_tbl2
SELECT DISTINCT id, text1, text2, text3
FROM dups
GROUP BY text1, text2
ORDER BY text3 DESC
見 MySQLはINSERT...選択 ます。
外部キーを削除せずに重複を削除する
create table tmp like mytable;
ALTER TABLE tmp ADD UNIQUE INDEX(text1, text2, text3, text4, text5, text6);
insert IGNORE into tmp select * from mytable;
delete from mytable where id not in ( select id from tmp);
新しいテーブルを作成できる場合は、text1 + text2フィールドに一意のキーを使用して作成します。次に、エラーを無視してテーブルに挿入します(INSERT IGNORE構文を使用):
select * from my_tbl order by text3 desc
- text3 descによる順序はNULLを最後に置くと思いますが、それを再確認してください。
これらのすべての列のインデックスは大いに役立つ可能性がありますが、今ではインデックスの作成がかなり遅くなる可能性があります。
重複の少ない大きなテーブルの場合、テーブル全体を別の場所にコピーしないようにすることができます。 1つの方法は、保持する行を保持する(重複するキーごとに)一時テーブルを作成し、元のテーブルから重複を削除することです。
こちらに例を示します。
MySQLの経験はあまりありません。分析機能がある場合は、次を試してください:
delete from my_tbl where id in ( select id from (select id, row_number() over (partition by text1, text2 order by text3 desc) as rn from my_tbl /* optional: where text1 like 'a%' */ ) as t2 where rn > 1 )
オプションのwhere句は、文字ごとに1回など、複数回実行する必要があることを意味します。text1にインデックスを作成しますか?
これを実行する前に、「text desc」がMySQLではNULLを最後にソートします。
これは古いスレッドであることは知っていますが、速度に関しては、100秒(10:1)ではなく10秒と言う、かなり高速でカスタマイズ可能なやや厄介なメソッドがあります。
私の方法では、避けようとしていた面倒のものをすべて必要としました:
- グループ化(およびグループ化)
- ORDER BYを使用したグループ連結
- 2つの一時テーブル
- ディスク上のファイルを使用する
- なんとか(php?)後にファイルを削除する
しかし、あなたが何百万(または私の場合は数千万)について話しているとき、それは価値があります。
とにかくコメントはポルトガル語で書かれているのであまり意味がありませんが、ここに私のサンプルがあります:
編集:コメントを受け取ったら、その仕組みをさらに説明します:)
START TRANSACTION;
DROP temporary table if exists to_delete;
CREATE temporary table to_delete as (
SELECT
-- escolhe todos os IDs duplicados menos os que ficam na BD
-- A ordem de escolha dos IDs é dada por "ORDER BY campo_ordenacao DESC" em que o primeiro é o que fica
right(
group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','),
length(group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','))
- locate(",",group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','))
) as ids,
count(*) as c
-- Tabela a eliminar duplicados
FROM teste_dup
-- campos a usar para identificar duplicados
group by test_campo1, test_campo2, teste_campoN
having count(*) > 1 -- é duplicado
);
-- aumenta o limite desta variável de sistema para o máx
SET SESSION group_concat_max_len=4294967295;
-- envia os ids todos a eliminar para um ficheiro
select group_concat(ids SEPARATOR ',') from to_delete INTO OUTFILE 'sql.dat';
DROP temporary table if exists del3;
create temporary table del3 as (select CAST(1 as signed) as ix LIMIT 0);
-- insere os ids a eliminar numa tabela temporaria a partir do ficheiro
load data infile 'sql.dat' INTO TABLE del3
LINES TERMINATED BY ',';
alter table del3 add index(ix);
-- elimina os ids seleccionados
DELETE teste_dup -- tabela
from teste_dup -- tabela
join del3 on id=ix;
COMMIT;
この単純なクエリを使用して、重複するエントリをすべて削除できます。 重複するレコードがすべて選択されて削除されます。
DELETE i1
FROM TABLE i1
LEFT JOIN TABLE i2
ON i1.id = i2.id
AND i1.colo = i2.customer_invoice_id
AND i1.id < i2.id
WHERE i2.customer_invoice_id IS NOT NULL