web-dev-qa-db-fra.com

La sous-requête MySQL ralentit considérablement, mais elles fonctionnent bien indépendamment

Querre 1:

select distinct email from mybigtable where account_id=345

prend 0,1S

Query 2:

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

prend 0,2s

Query 3:

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

prend 22 minutes et 90%, c'est dans l'état "préparant". Pourquoi cela prend-il tellement de temps?.

La table est innodub avec 3,2 millions de lignes sur MySQL 5.0

8
Stewie

Dans Query 3, vous exécutez essentiellement une sous-requête pour chaque rangée de mybigble contre lui-même.

Pour éviter cela, vous devez faire deux changements majeurs:

Changement majeur n ° 1: refacteur de la requête

Voici votre requête originale

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

Tu pourrais essayer

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 peut-être le comte par email

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;

Changement majeur n ° 2: indexation appropriée

Je pense que vous avez déjà cela depuis que la requête 1 et la requête 2 courent vite. Assurez-vous d'avoir un index composé sur (comptent_id, email). Faire SHOW CREATE TABLE mybigtable\G et assurez-vous d'en avoir un. Si vous ne l'avez pas ou si vous n'êtes pas sûr, créez-vous quand même l'index:

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

Mise à jour 2012-03-07 13:26 Est

Si vous voulez faire un non pas dans (), changer le INNER JOIN à un LEFT JOIN et vérifier le côté droit étant null, comme ceci:

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;

Mise à jour 2012-03-07 14:13 HNE

S'il vous plaît lire ces deux liens sur faire des jointures

Voici une excellente vidéo YouTube où j'ai appris à refacteur requis et au livre qu'il reposait sur

8
RolandoMySQLDBA

Dans MySQL, les sous-éléments de la clause de la clause sont ré-exécutés pour chaque rangée de la requête extérieure, créant ainsi O (n ^ 2). La courte histoire est, n'utilisez pas (sélectionnez).

9
Aaron Brown
  1. Avez-vous un index sur compte_id?

  2. Le deuxième problème peut être avec les sous-requêtes imbriquées qui ont une performance terrible en 5.0.

  3. Le groupe avec une clause ayant une clause est plus rapide que distinct.

  4. Qu'essayez-vous de faire ce qui peut être mieux fait par des jointures en plus du point n ° 3?

Il y a beaucoup de traitement impliqué lors de la manipulation d'une sous-requête () telle que la vôtre. Vous pouvez en savoir plus à ce sujet ici .

Ma première suggestion serait d'essayer de ré-écrire la sous-requête dans une jointure à la place. Quelque chose comme (non testé):

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