A subconsulta do MySQL diminui drasticamente, mas eles funcionam bem independentemente

8

Consulta 1:

select distinct email from mybigtable where account_id=345

leva 0.1s

Consulta 2:

Select count(*) as total from mybigtable where account_id=123 and email IN (<include all from above result>)

leva 0.2s

Consulta 3:

Select count(*) as total from mybigtable where account_id=123 and email IN (select distinct email from mybigtable where account_id=345)

leva 22 minutos e 90% está no estado "preparando". Por que isso leva tanto tempo?

Tabela é innodb com 3.2mil linhas no MySQL 5.0

Stewie
fonte

Respostas:

8

Na Consulta 3, você está basicamente executando uma subconsulta para cada linha do mybigtable contra si mesma.

Para evitar isso, você precisa fazer duas grandes alterações:

ALTERAÇÃO PRINCIPAL 1: Refatorar a consulta

Aqui está sua consulta original

Select count(*) as total from mybigtable
where account_id=123 and email IN
(select distinct email from mybigtable where account_id=345)

Você poderia tentar

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;

ou talvez a contagem por 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;

MAIOR ALTERAÇÃO # 2: Indexação Correta

Acho que você já tem isso desde que a consulta 1 e a consulta 2 são executadas rapidamente. Verifique se você tem um índice composto em (account_id, email). Faça SHOW CREATE TABLE mybigtable\Ge verifique se você tem um. Se você não o possui ou não tem certeza, crie o índice de qualquer maneira:

ALTER TABLE mybigtable ADD INDEX account_id_email_ndx (account_id,email);

UPDATE 2012-03-07 13:26 EST

Se você deseja fazer um NOT IN (), altere INNER JOINpara a LEFT JOINe verifique se o lado direito é NULL, assim:

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

Por favor, leia estes dois links sobre como fazer JOINs

Aqui está um ótimo vídeo do YouTube, onde aprendi a refatorar consultas e o livro em que ele foi baseado

RolandoMySQLDBA
fonte
9

No MySQL, as sub-seleções dentro da cláusula IN são reexecutadas para cada linha da consulta externa, criando assim O (n ^ 2). A história curta é: não use IN (SELECT).

Aaron Brown
fonte
1
  1. Você tem um índice em account_id?

  2. O segundo problema pode estar nas subconsultas aninhadas que apresentam um desempenho terrível no 5.0.

  3. GROUP BY com uma cláusula having é mais rápido que DISTINCT.

  4. O que você está tentando fazer, que pode ser melhor realizado através de junções, além do Item # 3?

Stephen Senkomago Musoke
fonte
1

Há muito processamento envolvido ao lidar com uma subconsulta IN () como a sua. Você pode ler mais sobre isso aqui .

Minha primeira sugestão seria tentar reescrever a subconsulta em um JOIN. Algo como (não testado):

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
Derek Downey
fonte