Причината се крие в използването на ИЛИ условия в КЪДЕ клауза.
За да илюстрирате, опитайте да изпълните заявката отново, този път само с id = 5
условие и получете (изход 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 не е много добър с обработката на множество индекси в една и съща заявка. Нещата са малко по-добри с условията И; е по-вероятно да видите index_merge оптимизация от индексен съюз оптимизация.
Нещата се подобряват с напредването на версиите, но тествах заявката ви за версия 5.5
, която е в текущата най-нова производствена версия и резултатите са такива, каквито описвате.
За да обясните защо това е трудно, помислете:два различни индекса ще отговорят за две различни условия на заявката. Един ще отговаря за id = 5
, другият за parent_id = @last_id OR parent_id = 5
(BTW няма проблем с ИЛИ вътре в последния, тъй като и двата термина се обработват от един и същ индекс).
Няма единен индекс, който да отговаря и за двете, и следователно за FORCE INDEX
инструкцията се игнорира. Вижте, FORCE INDEX
казва, че MySQL трябва да използва an индекс върху сканиране на таблица. Това не означава, че трябва да използва повече от един индекс при сканиране на таблица.
Така че MySQL следва правилата на документацията тук. Но защо това е толкова сложно? Тъй като за да отговори с помощта на двата индекса, MySQL трябва да събира резултати и от двата, да съхранява единия настрана в някакъв временен буфер, докато управлява втория. След това трябва да преминете през този буфер, за да филтрирате идентични редове (възможно е някой ред да отговаря на всички условия). И след това да сканира този буфер, за да върне резултатите.
Но изчакайте, този буфер сам по себе си не е индексиран. Филтрирането на дубликати не е очевидна задача. Така че MySQL предпочита да работи върху оригиналната таблица и да сканира там и да избягва цялата тази бъркотия.
Разбира се, това е разрешимо. Инженерите в Oracle може би тепърва ще подобрят това (напоследък работят усилено за подобряване на плановете за изпълнение на заявки), но не знам дали това е на задачата TODO, или има висок приоритет.