Powód leży w użyciu LUB warunki w GDZIE klauzula.
Aby to zilustrować, spróbuj ponownie uruchomić zapytanie, tym razem tylko z id = 5
warunek i uzyskaj (wyjaśnij dane wyjściowe):
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| 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 |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
I znowu, tym razem tylko z parent_id = @last_id OR parent_id = 5
stan i uzyskaj:
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| 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 nie radzi sobie zbyt dobrze z obsługą wielu indeksów w tym samym zapytaniu. Sytuacja jest nieco lepsza z warunkami AND; bardziej prawdopodobne jest, że zobaczysz index_merge optymalizacja niż unia indeksów optymalizacja.
Sprawy się poprawiają wraz z rozwojem wersji, ale przetestowałem Twoje zapytanie w wersji 5.5
, który jest w najnowszej wersji produkcyjnej, a wyniki są zgodne z opisem.
Aby wyjaśnić, dlaczego jest to trudne, rozważ:dwa różne indeksy odpowiedzą na dwa różne warunki zapytania. Jeden odpowie za id = 5
, drugi dla parent_id = @last_id OR parent_id = 5
(Przy okazji nie ma problemu z LUB wewnątrz tego ostatniego, ponieważ oba terminy są obsługiwane z tego samego indeksu).
Nie ma jednego indeksu, który może odpowiadać na oba, dlatego FORCE INDEX
instrukcja jest ignorowana. Zobacz, FORCE INDEX
mówi, że MySQL musi używać an indeks na skan tabeli. Nie oznacza to, że podczas skanowania tabeli musi być używany więcej niż jeden indeks.
Tak więc MySQL postępuje zgodnie z zasadami dokumentacji tutaj. Ale dlaczego jest to takie skomplikowane? Ponieważ aby odpowiedzieć przy użyciu obu indeksów, MySQL musi zbierać wyniki z obu, przechowywać jeden z nich w jakimś tymczasowym buforze podczas zarządzania drugim. Następnie musi przejść przez ten bufor, aby odfiltrować identyczne wiersze (możliwe, że jakiś wiersz spełnia wszystkie warunki). A następnie przeskanować ten bufor, aby zwrócić wyniki.
Ale czekaj, ten bufor sam w sobie nie jest indeksowany. Filtrowanie duplikatów nie jest zadaniem oczywistym. Dlatego MySQL woli pracować na oryginalnej tabeli i wykonywać tam skanowanie, unikając całego tego bałaganu.
Oczywiście można to rozwiązać. Inżynierowie z Oracle mogą jeszcze to poprawić (niedawno ciężko pracowali nad poprawą planów wykonywania zapytań), ale nie wiem, czy jest to zadanie TODO, czy ma wysoki priorytet.