2016-07-21 10 views
-1

私は以下の自己結合クエリを持っている:のMySql - 自己が参加 - 全表スキャン(インデックスをスキャンすることはできません)

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 

クエリが非常に遅く、実行計画を見た後、原因があるように思われますJOINのフル・テーブル・スキャン。テーブルには500行しかありませんが、これが問題であると疑われて、オプティマイザの選択に違いがあるかどうかを確認するために、100,000行に増やしました。 100k行ではまだ完全なテーブルスキャンを行っていませんでした。

私の次のステップは、次のクエリでのインデックスを試してみて、力になったが、同じ状況が発生し、全表スキャン:すべての列(ID、LFT、RGT)は

SELECT A.id 
FROM categories_nested_set  AS A 
LEFT JOIN categories_nested_set AS B 
FORCE INDEX (idx_lft, idx_rgt) 
ON (A.lft BETWEEN B.lft AND B.rgt) 

Execution plan for full table scan query :/

整数であり、すべてインデックスされます。

ここで、MySqlがフルテーブルスキャンを実行するのはなぜですか?

テーブル全体のスキャンではなくインデックスを使用するようにクエリを変更するにはどうすればよいですか?あなたはたくさんのインデックスのを持っている

CREATE TABLE mytbl (lft int(11) NOT NULL DEFAULT '0', 
rgt int(11) DEFAULT NULL, 
id int(11) DEFAULT NULL, 
category varchar(128) DEFAULT NULL, 
    PRIMARY KEY (lft), 
    UNIQUE KEY id (id), 
    UNIQUE KEY rgt (rgt), 
    KEY idx_lft (lft), 
    KEY idx_rgt (rgt)) ENGINE=InnoDB DEFAULT CHARSET=utf8 

おかげ

+0

共有ショー 'の結果は、各関連XYZについては、以下 – Drew

+0

結果テーブルxyz'を作成する:クエリの固定された未定着バージョン間機能を説明する比較 '表mytblをCREATEを( LFTはint(11)NOT NULL DEFAULT '0'、 RGT int型(11)DEFAULT NULLを、 ID int型(11)DEFAULT NULLを、 カテゴリVARCHAR(128)のDEFAULT NULL、 PRIMARY KEY(LFT)、 UNIQUEキーID (id)、 ユニークキーrgt(rgt)、 KEY idx_l KEY idx_rgt(rgt) )ENGINE = InnoDB DEFAULT CHARSET = utf8' – mils

+0

'PRIMARY KEY'は' UNIQUE'キーが 'KEY'です。したがって、2つのKEYは重複しており、削除する必要があります。 –

答えて

-1

隣接リストとインデックスの組み合わせについてはほとんど情報があるとして、次のSOの質問は、解決するために重要である:

MySQL & nested set: slow JOIN (not using index)

それは基本的な比較条件を追加することの使用をトリガーすることが表示されますインデックスは次のようになります。

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B ON (A.lft BETWEEN B.lft AND B.rgt) 
-- THE FOLLOWING DUMMY CONDITIONS TRIGGER INDEX 
WHERE A.lft > 0 
AND B.lft > 0 
AND B.rgt > 0 

これ以上のテーブルスキャンはありません。

EDIT: EXPLAIN function results, top is fixed, bottom is not

+0

「修正」の有無にかかわらず、以下の項目をテストしてください: 'FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%'; '数字が同じ場合は、まだフルスキャンですが、テーブルではなくインデックスに表示されます。 –

+0

おかげリック、以下の数字(ゼロ番号除外):FIX WITH 'Handler_commit'、 '1' 'Handler_external_lock'、4 '' 'Handler_read_first'、 '2' は 'Handler_read_key'、 '2' FIX WITHOUT 'Handler_read_next'、 '646' 'Handler_commit'、 '1' 'Handler_external_lock'、4 '' 'Handler_read_first'、 '72' 'Handler_read_key'、 '72' 'Handler_read_rnd_next'、」 37941 ' – mils

+0

これは、修正が助けになったと私に確信させる。 –

2

は、そのうちのいくつかは冗長です。それらのいくつかをクリアして始めましょう。インデックスが多すぎると挿入と更新が遅くなります。

PRIMARY KEY (lft), 
KEY idx_lft (lft), 

すでにlftにプライマリキーが定義されているので、lftの別のインデックスにはこれ以上必要なものはありません。同様に、rgt上のユニークなインデックスと同様に、以下にリストされる第2のインデックスは必要ありません。

UNIQUE KEY rgt (rgt), 
KEY idx_rgt (rgt) 

ここでクエリを見てみましょう。

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 

これは、野生で遭遇する可能性の高いクエリではありません。 500行では、このクエリは5000行も生成する可能性がありますか?一度に作成されたキー全体が本当に必要ですか?このクエリが遅い理由は、mysqlが定数の場合にのみoptimize range comparisionsになるためです。実際のクエリは次のようになります。

SELECT B.* 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 
WHERE a.id = N; 

ここで、特定のIDのノードを作成します。これはインデックスを使用し、非常に高速になります。クエリを最適化する際のポイントは何ですか?それではあまり使用しないでください。

+0

返信いただきありがとうございます、私はいくつかの追加情報で私の質問を更新しました。基本的には、より大きなJOINの一部であるため、WHERE節ではできません。私は簡単にするために、この質問のためにそれを取り除いた。そしてJOINの実際のユースケースでは、インデックスを使用しません。より大きいJOINのシナリオでは、このコンシューマは範囲比較のために定数ですか、それともユーザー定義の定数でなければなりませんか?このシナリオでテーブルスキャンを回避するにはどうすればよいですか?ありがとう – mils

+0

それはゴールポストを移動し、それを遠くに移動しています – e4c5

+0

私はいくつかのパフォーマンステストを行いました。このmytblのサイズは、私のデータを別のシステムにロードする際に最大の影響を与えます。 500の代わりに10k行があると、パフォーマンスは6000%低下します。今は4時間かかり、悪化するだけです。だから、MySqlに範囲クエリのインデックスを使用させる方法を知っていただければ幸いです。 – mils

関連する問題