web-dev-qa-db-ja.com

MySQLの同じテーブルでサブクエリを使用するにはどうすればよいですか?

このようなクエリの実行に非常に長い時間がかかります。テーブルは約400万行です。

DELETE FROM TABLE WHERE value_was IS NULL OR value_was <= value_now;

(value_was、value_now)のインデックスを作成して、次のようなことができるといいのですが

DELETE FROM TABLE WHERE 
ID1 IN (SELECT ID1 from TABLE where value_was IS NULL) 
OR ID2 IN (SELECT ID2 FROM TABLE WHERE value_was <= value_now);

このテーブルには主キーがありません。 2つの複合キーがあります。また、同じテーブルをサブクエリで使用できないと思いますが、最初のクエリのパフォーマンスを向上させるにはどうすればよいですか?

どうもありがとうございました。

1
toy

残念ながら、MySQLは、特に非SELECTクエリでのサブクエリの問題で非常に悪名高くなっています。私はかつてこれをFeb 22, 2011で対処しました: MySQLサブクエリの問題

その古い投稿で、MySQLがサブクエリを処理する方法に関するドキュメントを見つけました。 Oct 20, 2012の投稿を最後に最新のドキュメントで編集したので、これに追いつけようとしました。

つまり、サブクエリの最適化では、EXPLAINプランを理解するために、行が断続的に消えることがあります。 ドキュメントを読んで、それについて学ぶことができますそれを包み込むことができる場合(再びそれを繰り返さない)

さてあなたの質問のために...

最も賢明なアプローチ、JOIN、サブSELECTはありません。

DELETE FROM TABLE WHERE value_was IS NULL OR value_was <= value_now;

見てください。それは質問からのあなたの質問です。なぜあなたの元のアイデアを提案するのですか?

これは、1回のパスでの全表スキャンです。他のアプローチでは、作業が2倍になる可能性があります(ゲームの後半でインデックスを使用しようとすると、3倍になります)。この方法で実行すると、テーブルを最適化する必要も遅くなります。

削除してデフラグする場合は、2つのオプションがあります。

オプション1

MyISAM

DELETE FROM `TABLE` WHERE value_was IS NULL OR value_was <= value_now;
ALTER TABLE `TABLE` ENGINE=MyISAM;

InnoDB

DELETE FROM `TABLE` WHERE value_was IS NULL OR value_was <= value_now;
ALTER TABLE `TABLE` ENGINE=InnoDB;

オプション#2

DELETE FROM `TABLE` WHERE value_was IS NULL OR value_was <= value_now;
CREATE TABLE `NEWTABLE`
SELECT * FROM `TABLE`
WHERE NOT (value_was IS NULL OR value_was <= value_now);
DROP TABLE `TABLE`;
ALTER TABLE `NEWTABLE` RENAME `TABLE`;

警告

何かをする前に、このカウントを実行します

SELECT COUNT(1) INTO @Count_All FROM `TABLE`;
SELECT COUNT(1) INTO @Count_Zap FROM `TABLE`
WHERE value_was IS NULL OR value_was <= value_now;
SET @DeletePct = @Count_Zap * 100 / @Count_All;
SELECT @DeletePct;

@DeletePctは、DELETEを実行した場合に削除されるテーブルのパーセンテージです。

パーセンテージが低すぎる場合は、DELETE FROMTABLEWHERE value_was IS NULL OR value_was <= value_now;で十分です。デフラグは待機できます。それ以外の場合は、オプションの1つを選択するか、テーブルの行の断片化を利用できます。

ちなみに、インデックスを使用したい場合は、テーブルをデフラグした後で使用してください。

0
RolandoMySQLDBA

あなたは提案を求めました。ここにある。

このケースを分析する場合、最初に行うことは、2つの個別のDELETEステートメントに分離することです。1つはIS NULLケースをカバーし、もう1つはvalue_was <をカバーしますValue_nowの場合。両方とも動作が遅いかどうかを確認します。IS NULLの場合は、インデックスが以前とは異なる方法で動作しない限り、テーブルスキャンを実行すると予想されます。これは遅くなります。

適切なインデックスを設定することで他のケースを迅速化できるかどうかは、調査の主な焦点になります。

0
Walter Mitty

INの代わりにEXISTS句を使用することもできます。これは、通常、パフォーマンスが低下するためです。

  DELETE FROM TABLE T_delete
  WHERE EXISTS (SELECT 1
                FROM TABLE t_join
                WHERE value_was IS NULL
                AND T_delete.ID1 = t_join.ID1) 
  OR EXISTS (SELECT 1
                FROM TABLE t_join
                WHERE value_was <= value_now
                AND T_delete.ID2 = t_join.ID2);

パフォーマンスが向上するかどうかはわかりませんが、ORを使用する代わりに、このクエリを2つに分割することもできます。

EXISTS here を使用したクエリの最適化に関する詳細情報があります。

0
listik