データベースに関しては、私は比較的初心者です。私たちは MySQL を使用しており、現在、実行に時間がかかると思われる SQL ステートメントを高速化しようとしています。SO で同様の質問を探しましたが、見つかりませんでした。
目標は、テーブル B で一致する ID を持つテーブル A のすべての行を削除することです。
現在、次のことを行っています。
DELETE FROM a WHERE EXISTS (SELECT b.id FROM b WHERE b.id = a.id);
テーブル a には約 100K 行、テーブル b には約 22K 行があります。列「id」は、両方のテーブルの PK です。
このステートメントは、私のテスト ボックス (Pentium D、XP SP3、2GB RAM、MySQL 5.0.67) で実行するのに約 3 分かかります。これは私には遅いようです。そうではないかもしれませんが、私は物事をスピードアップしたいと思っていました。これを達成するためのより良い/より速い方法はありますか?
編集:
役に立つかもしれないいくつかの追加情報。テーブル A と B の構造は、テーブル B を作成するために次の手順を実行した場合と同じです。
CREATE TABLE b LIKE a;
テーブル a (およびテーブル b) には、それに対して行われるクエリを高速化するのに役立ついくつかのインデックスがあります。繰り返しますが、私は DB 作業の比較的初心者であり、まだ学習中です。これが物事にどの程度の影響を与えるかはわかりません。インデックスもクリーンアップする必要があるため、効果があると思いますよね?また、速度に影響を与える可能性のある他のDB設定があるかどうかも疑問に思っていました.
また、私はINNO DBを使用しています。
ここに、あなたに役立つかもしれないいくつかの追加情報があります。
テーブル A の構造は次のようになります (これを少しサニタイズしました)。
DROP TABLE IF EXISTS `frobozz`.`a`;
CREATE TABLE `frobozz`.`a` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`fk_g` varchar(30) NOT NULL,
`h` int(10) unsigned default NULL,
`i` longtext,
`j` bigint(20) NOT NULL,
`k` bigint(20) default NULL,
`l` varchar(45) NOT NULL,
`m` int(10) unsigned default NULL,
`n` varchar(20) default NULL,
`o` bigint(20) NOT NULL,
`p` tinyint(1) NOT NULL,
PRIMARY KEY USING BTREE (`id`),
KEY `idx_l` (`l`),
KEY `idx_h` USING BTREE (`h`),
KEY `idx_m` USING BTREE (`m`),
KEY `idx_fk_g` USING BTREE (`fk_g`),
KEY `fk_g_frobozz` (`id`,`fk_g`),
CONSTRAINT `fk_g_frobozz` FOREIGN KEY (`fk_g`) REFERENCES `frotz` (`g`)
) ENGINE=InnoDB AUTO_INCREMENT=179369 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
問題の一部は、このテーブルに多数のインデックスがあることにあると思われます。表 B は表 B と似ていますが、列id
としか含まれていませんh
。
また、プロファイリングの結果は次のとおりです。
starting 0.000018
checking query cache for query 0.000044
checking permissions 0.000005
Opening tables 0.000009
init 0.000019
optimizing 0.000004
executing 0.000043
end 0.000005
end 0.000002
query end 0.000003
freeing items 0.000007
logging slow query 0.000002
cleaning up 0.000002
解決した
すべての応答とコメントに感謝します。彼らは確かに私にその問題について考えさせました。「a.id を参照する他のテーブルはありますか?」という簡単な質問をすることで、問題を回避できるようにしてくれたdotjoeに感謝します。
問題は、テーブル A に、他の 2 つのテーブル C と D を更新するストアド プロシージャを呼び出す DELETE TRIGGER があったことです。 、それは声明を持っていました、
DELETE FROM c WHERE c.id = theId;
EXPLAINステートメントを調べて、これを次のように書き直しました。
EXPLAIN SELECT * FROM c WHERE c.other_id = 12345;
そのため、これが何をしているのかを見ることができ、次の情報が得られました。
id 1
select_type SIMPLE
table c
type ALL
possible_keys NULL
key NULL
key_len NULL
ref NULL
rows 2633
Extra using where
これは、作成するのが骨の折れる操作であり、22500回呼び出されることになるため(削除される特定のデータセットに対して)、それが問題であることがわかりました。その other_id 列に INDEX を作成し、EXPLAIN を再実行すると、次のようになりました。
id 1
select_type SIMPLE
table c
type ref
possible_keys Index_1
key Index_1
key_len 8
ref const
rows 1
Extra
はるかに良い、実際には本当に素晴らしい.
Index_1 と私の削除時間は、mattkempによって報告された時間と一致していることを追加しました。これは、土壇場でいくつかの追加機能を追加したため、私の側では本当に微妙なエラーでした. ダニエルが述べたように、提案された代替の DELETE/SELECT ステートメントのほとんどは、本質的に同じ時間を要したことが判明しました。私はする必要がありました。この別のテーブル C にインデックスを指定すると、DELETE が高速になりました。
事後分析:
この演習から 2 つの教訓が得られました。まず、SQL クエリの影響をよりよく理解するために、EXPLAIN ステートメントの機能を活用していないことは明らかです。これは初歩的なミスなので、自分を責めるつもりはありません。私はその間違いから学びます。第二に、問題のあるコードは「すぐにやり遂げる」という考え方の結果であり、不適切な設計/テストにより、この問題がすぐに現れなかった. この新しい機能のテスト入力として使用するかなり大きなテスト データ セットをいくつか生成していれば、自分の時間もあなたの時間も無駄にはならなかったでしょう。DB 側での私のテストには、アプリケーション側の深さが欠けていました。今、私はそれを改善する機会を得ました。