De reden ligt in het gebruik van OF voorwaarden in de WAAR clausule.
Probeer ter illustratie de query opnieuw uit te voeren, deze keer met alleen de id = 5
voorwaarde, en krijg (UITLEG output):
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| 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 |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
En nogmaals, deze keer met alleen de parent_id = @last_id OR parent_id = 5
voorwaarde, en ontvang:
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| 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 is niet zo goed in het verwerken van meerdere indexen in dezelfde query. Met AND-voorwaarden gaat het iets beter; men heeft meer kans om een index_merge optimalisatie dan een index union optimalisatie.
Dingen verbeteren naarmate de versies vorderden, maar ik heb je vraag getest op versie 5.5
, de huidige laatste productieversie, en de resultaten zijn zoals u beschrijft.
Overweeg om uit te leggen waarom dit moeilijk is:twee verschillende indexen beantwoorden voor twee verschillende voorwaarden van de query. Men zal antwoorden voor id = 5
, de andere voor parent_id = @last_id OR parent_id = 5
(BTW geen probleem met de OF binnen de laatste, aangezien beide termen worden behandeld vanuit dezelfde index).
Er is geen enkele index die beide kan beantwoorden, en daarom is de FORCE INDEX
instructie wordt genegeerd. Zie, FORCE INDEX
zegt dat MySQL een . moet gebruiken index over een tabelscan. Het betekent niet dat het meer dan één index moet gebruiken voor een tabelscan.
Dus MySQL volgt de regels van de documentatie hier. Maar waarom is dit zo ingewikkeld? Omdat MySQL, om beide indexen te gebruiken, resultaten van beide moet verzamelen, de ene opzij moet zetten in een tijdelijke buffer terwijl de tweede wordt beheerd. Dan moet het over die buffer gaan om identieke rijen eruit te filteren (het is mogelijk dat een rij aan alle voorwaarden voldoet). En dan die buffer scannen om de resultaten te retourneren.
Maar wacht, die buffer is op zich niet geïndexeerd. Het filteren van duplicaten is geen voor de hand liggende taak. Dus MySQL werkt het liefst op de originele tafel en scant daar, en vermijdt al die rommel.
Dit is natuurlijk op te lossen. De ingenieurs van Oracle kunnen dit nog verbeteren (de laatste tijd hebben ze hard gewerkt aan het verbeteren van de uitvoeringsplannen voor query's), maar ik weet niet of dit de TODO-taak is, of dat het een hoge prioriteit heeft.