その理由は、 WHERE句でのOR条件の使用にあります。
説明のために、今度はid = 5
条件のみを使用してクエリを再度実行し、get (EXPLAIN 出力)を試します。
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 1 | PRIMARY | tree | const | PRIMARY,index_both | PRIMARY | 4 | const | 1 | |
| 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
繰り返しますが、今回はparent_id = @last_id OR parent_id = 5
条件のみで、次を取得します。
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 1 | PRIMARY | tree | ALL | index_parent_id | NULL | NULL | NULL | 10 | Using where |
| 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
MySQL は、同じクエリで複数のインデックスを処理するのが得意ではありません。AND 条件を使用すると、状況がわずかに改善されます。インデックス ユニオンの最適化よりもindex_mergeの最適化が見られる可能性が高くなります。
バージョンが進むにつれて状況は改善されています5.5
が、現在の最新の製品バージョンである version でクエリをテストしたところ、結果は説明どおりです。
これが難しい理由を説明するために、次のことを考慮してください。クエリの 2 つの異なる条件に対して、2 つの異なるインデックスが応答します。1つはid = 5
、もう1つは(両方の用語が同じインデックス内から処理されるため、後者のORparent_id = @last_id OR parent_id = 5
で問題はありません)。
両方に対応できる単一のインデックスは存在しないため、FORCE INDEX
命令は無視されます。参照してください、FORCE INDEX
MySQL はテーブル スキャンでインデックスを使用する必要があると言います。テーブルスキャンで複数のインデックスを使用する必要があることを意味するものではありません。
そのため、MySQL はここのドキュメントの規則に従います。しかし、なぜこれがそれほど複雑なのですか?両方のインデックスを使用して応答するため、MySQL は両方から結果を収集する必要があるため、2 番目のインデックスを管理している間、一方を一時バッファに保存します。次に、そのバッファを調べて、同一の行を除外する必要があります (一部の行がすべての条件に適合する可能性があります)。そして、そのバッファをスキャンして結果を返します。
しかし、待ってください、そのバッファ自体はインデックス化されていません。重複をフィルタリングすることは、明らかな作業ではありません。そのため、MySQL は元のテーブルで作業し、そこでスキャンを実行して、混乱を避けることを好みます。
もちろん、これは解決可能です。オラクルのエンジニアはまだこれを改善している可能性があります (最近、彼らはクエリ実行計画の改善に熱心に取り組んでいます) が、これが TODO タスクにあるのか、それとも優先度が高いのかはわかりません。