Mechanizm planowania zapytań w MySQL jest mniej sprytny niż sądziłem. Chciałem wykonać z pozoru łatwe zapytanie: z bazy użytkowników liczącej ok 130 tys. wierszy chciałem wyłuskać osoby mieszkające pod określonymi kodami pocztowymi (ok. 20 tys.). Dodatkowym warunkiem było podanie numeru telefonu, który znajduje się w innej tabelce oraz wyrażenie zgody na marketing, ale to nie jest istotne dla głównego problemu. Napisałem takie zapytanie:
SELECT k.imie, k.nazwisko, k.kod_pocztowy, u.numer_telefonu FROM klient k INNER JOIN ustawienia u ON k.id = u.klient_id INNER JOIN tmp_kody t ON k.kod_pocztowy = t.kod_pocztowy WHERE k.zgoda_na_marketing = 1 ORDER BY k.id
Szukane kody pocztowe wrzuciłem sobie do tabelki, bo przy próbie umieszczenia ich w treści zapytania phpMyAdmin odmówił współpracy, a wklejanie zapytania na zdalnej konsoli trwało ponad 10 minut.
Pierwszą próbę wykonania zapytania przerwałem po kilku minutach. EXPLAIN pokazał, że MySQL nie bardzo chce korzystać z indeksów i najchętniej przeskanowałby całą tabelkę tmp_kody i ustawienia. Próbowałem przepisać zapytanie na kilka sposobów by skłonić MySQL do ułożenia innego planu, ale bezskutecznie. Skutecznym rozwiązaniem okazało się tymczasowe dodanie indeksu na kolumnę kod_pocztowy. Oryginalne zapytanie wykonało się w mgnieniu oka, a EXPLAIN pokazał, że wszystkie etapy planu wykonania zaczęły korzystać z indeksów.
Wniosek na przyszłość: jeśli MySQL sobie nie radzi to można mu pomóc dodając tymczasowy indeks. To może okazać się szybsze niż czekanie na wynik nieoptymalnego zapytania.