MySQL ma znane problemy z optymalizacją zapytań obejmujących skorelowane podzapytania lub podselekcje. Aż do wersji 5.6.5 nie materializuje podzapytań, jednak zmaterializuje tabelę pochodną używaną w łączeniu.
W istocie oznacza to, że kiedy używasz złączenia, przy pierwszym napotkaniu podzapytania MySQL wykona następujące czynności:
SELECT code1 FROM myTable GROUP BY code1 HAVING COUNT(code1) > 1
I przechowuj wyniki w tabeli tymczasowej (która jest zaszyfrowana, aby przyspieszyć wyszukiwanie), a następnie dla każdej wartości w myTable
przeszuka tabelę tymczasową, aby sprawdzić, czy kod tam jest.
Jednak od kiedy używasz IN
podzapytanie nie jest zmaterializowane i jest przepisywane w następujący sposób:
SELECT t1.code1, t1.code2
FROM myTable t1
WHERE EXISTS
( SELECT t2.code1
FROM myTable t2
WHERE t2.Code1 = t1.Code1
GROUP BY t2.code1
HAVING COUNT(t2.code1) > 1
)
Co oznacza, że dla każdego code
w myTable
, ponownie uruchamia podzapytanie. Co w przypadku bardzo wąskiego zapytania zewnętrznego jest w porządku, ponieważ bardziej wydajne jest uruchomienie podzapytania tylko kilka razy, niż uruchomienie go dla wszystkich wartości i przechowywanie wyników w tabeli tymczasowej, jednak gdy zapytanie zewnętrzne jest szerokie, skutkuje w wewnętrznym zapytaniu wykonywanym wiele razy i tutaj pojawia się różnica w wydajności.
Więc jeśli chodzi o liczbę wierszy, zamiast uruchamiać podzapytanie ~30 000 razy, uruchamiasz je raz, a następnie wyszukujesz ~30 000 wierszy w zaszyfrowanej tabeli tymczasowej zawierającej tylko 400 wierszy. Stanowiłoby to tak drastyczną różnicę w wydajności.
Ten artykuł w dokumentacji online objaśnia optymalizację podzapytań znacznie bardziej szczegółowo.