MySQL subquery rallenta drasticamente, ma funzionano bene in modo indipendente
-
16-10-2019 - |
Domanda
Domanda 1:
select distinct email from mybigtable where account_id=345
prende 0.1s
Domanda 2:
Select count(*) as total from mybigtable where account_id=123 and email IN (<include all from above result>)
prende 0.2s
Domanda 3:
Select count(*) as total from mybigtable where account_id=123 and email IN (select distinct email from mybigtable where account_id=345)
richiede 22 minuti e 90% sua nello stato "preparazione". Perché questo prende così tanto tempo.
Tabella è InnoDB con file 3.2mil su MySQL 5.0
Soluzione
In Query 3, si sono fondamentalmente eseguendo una subquery per ogni riga di mybigtable contro se stessa.
Per evitare questo, è necessario fare due importanti modifiche:
grande cambiamento # 1: Refactor Query
Ecco la vostra query originale
Select count(*) as total from mybigtable
where account_id=123 and email IN
(select distinct email from mybigtable where account_id=345)
Si potrebbe provare
select count(*) EmailCount from
(
select tbl123.email from
(select email from mybigtable where account_id=123) tbl123
INNER JOIN
(select distinct email from mybigtable where account_id=345) tbl345
using (email)
) A;
o forse il conteggio per e-mail
select email,count(*) EmailCount from
(
select tbl123.email from
(select email from mybigtable where account_id=123) tbl123
INNER JOIN
(select distinct email from mybigtable where account_id=345) tbl345
using (email)
) A group by email;
grande cambiamento # 2: corretta indicizzazione
Penso che tu abbia questo già dal Query 1 e Query 2 correre veloce. Assicurarsi di avere un indice composto sul (ACCOUNT_ID, e-mail). Fare SHOW CREATE TABLE mybigtable\G
e assicuratevi di avere uno. Se non ce l'hai, o se non siete sicuri, quindi si crea l'indice in ogni caso:
ALTER TABLE mybigtable ADD INDEX account_id_email_ndx (account_id,email);
UPDATE 2012-03-07 13:26 EST
Se si vuole fare un NOT IN (), modificare il INNER JOIN
ad un LEFT JOIN
e controllare per il lato destro essendo NULL, in questo modo:
select count(*) EmailCount from
(
select tbl123.email from
(select email from mybigtable where account_id=123) tbl123
LEFT JOIN
(select distinct email from mybigtable where account_id=345) tbl345
using (email)
WHERE tbl345.email IS NULL
) A;
UPDATE 2012-03-07 14:13 EST
Si prega di leggere questi due link sul fare join
Qui è un grande video di YouTube dove ho imparato a query refactoring e il libro si basava su
Altri suggerimenti
In MySQL, sottoselezioni all'interno della clausola sono ri-eseguito per ogni riga della query esterna, creando così O (n ^ 2). Il racconto è, non utilizzare IN (SELECT).
-
Avete un indice su ACCOUNT_ID?
-
Il secondo problema potrebbe essere con le nidificati sub-query che hanno prestazioni terribile in 5.0.
-
GROUP BY con una clausola having è più veloce di DISTINCT.
-
Che cosa stai cercando di fare che può essere meglio fatto attraverso unisce in aggiunta alla voce # 3?
C'è un sacco di elaborazione coinvolti nella manipolazione di una subquery IN () come la vostra. Si può leggere di più su di esso qui .
Il mio primo suggerimento sarebbe quello di tentare di riscrivere il subquery in una JOIN invece. Qualcosa di simile (non testato):
SELECT COUNT(*) AS total FROM mybigtable AS t1
INNER JOIN
(SELECT DISTINCT email FROM mybigtable WHERE account_id=345) AS t2
ON t2.email=t1.email
WHERE account_id=123