La subconsulta MySQL se ralentiza drásticamente, pero funcionan bien independientemente

8

Consulta 1:

select distinct email from mybigtable where account_id=345

toma 0.1s

Consulta 2:

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

toma 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)

tarda 22 minutos y el 90% está en el estado de "preparación". ¿Por qué esto toma tanto tiempo?

La tabla es innodb con 3.2mil filas en MySQL 5.0

Stewie
fuente

Respuestas:

8

En la consulta 3, básicamente está ejecutando una subconsulta para cada fila de mybigtable contra sí misma.

Para evitar esto, debe hacer dos cambios importantes:

CAMBIO PRINCIPAL # 1: Refactorizar la consulta

Aquí está tu consulta original

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

Tu podrías intentar

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 tal vez el recuento por correo electrónico

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;

CAMBIO PRINCIPAL # 2: indexación adecuada

Creo que ya tiene esto ya que la Consulta 1 y la Consulta 2 se ejecutan rápidamente. Asegúrese de tener un índice compuesto en (account_id, email). Haz SHOW CREATE TABLE mybigtable\Gy asegúrate de tener uno. Si no lo tiene o si no está seguro, cree el índice de todos modos:

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

ACTUALIZACIÓN 2012-03-07 13:26 EST

Si desea hacer un NOT IN (), cambie el INNER JOINa ay LEFT JOINverifique que el lado derecho sea NULL, de esta manera:

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;

ACTUALIZACIÓN 2012-03-07 14:13 EST

Lea estos dos enlaces sobre cómo hacer JOIN

Aquí hay un gran video de YouTube donde aprendí a refactorizar consultas y el libro en el que se basó

RolandoMySQLDBA
fuente
9

En MySQL, las subselecciones dentro de la cláusula IN se vuelven a ejecutar para cada fila en la consulta externa, creando así O (n ^ 2). La historia corta es, no use IN (SELECCIONAR).

Aaron Brown
fuente
1
  1. ¿Tiene un índice en account_id?

  2. El segundo problema puede ser con las subconsultas anidadas que tienen un rendimiento terrible en 5.0.

  3. GROUP BY con una cláusula have es más rápido que DISTINCT.

  4. ¿Qué está tratando de hacer que se puede hacer mejor a través de combinaciones además del Artículo # 3?

Stephen Senkomago Musoke
fuente
1

Hay mucho procesamiento involucrado al manejar una subconsulta IN () como la suya. Puedes leer más sobre esto aquí .

Mi primera sugerencia sería intentar reescribir la subconsulta en JOIN. Algo como (no probado):

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
fuente