MySQL ha problemi noti con l'ottimizzazione delle query che coinvolgono sottoquery correlate o subselect. Fino alla versione 5.6.5 non materializza le sottoquery, tuttavia materializza una tabella derivata utilizzata in un join.
In sostanza ciò significa che quando si utilizza un join, la prima volta che viene incontrata la sottoquery MySQL eseguirà quanto segue:
SELECT code1 FROM myTable GROUP BY code1 HAVING COUNT(code1) > 1
E mantieni i risultati in una tabella temporanea (che è sottoposta a hash per rendere le ricerche più veloci), quindi per ogni valore in myTable
cercherà nella tabella temporanea per vedere se il codice è presente.
Tuttavia, da quando usi IN
la sottoquery non viene materializzata e viene riscritta come:
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
)
Ciò significa che per ogni code
in myTable
, esegue nuovamente la sottoquery. Che quando la tua query esterna è molto stretta va bene, poiché è più efficiente eseguire la sottoquery solo poche volte, piuttosto che eseguirla per tutti i valori e archiviare i risultati in una tabella temporanea, tuttavia quando la tua query esterna è ampia, risulta nella query interna in esecuzione molte volte, ed è qui che entra in gioco la differenza di prestazioni.
Quindi, per il conteggio delle righe, invece di eseguire la sottoquery circa 30.000 volte, la esegui una volta, quindi cerca circa 30.000 righe in una tabella temporanea con hash con solo 400 righe. Ciò spiegherebbe una differenza di prestazioni così drastica.
Questo articolo nei documenti online spiega l'ottimizzazione delle sottoquery in modo molto più approfondito.