2010-08-05 4 views
12

MISE À JOUR:Cette requête SELECT prend 180 secondes pour terminer

Juste pour le mentionner sur un endroit plus visible. Lorsque j'ai changé IN pour =, le temps d'exécution de la requête est passé de 180 à 0,00008 secondes. Différence de vitesse ridicule.


Cette requête SQL prend 180 secondes pour se terminer! Comment est-ce possible? Y a-t-il un moyen de l'optimiser pour être plus rapide? Il n'y a que 5 000 lignes dans chaque table, donc il ne devrait pas être si lent.

+5

Toute différence si vous changez le '' IN' à '=? –

+7

Ces noms de colonne blessent mon cerveau. Pouvez-vous expliquer ce que la requête est supposée faire? – RedFilter

+0

@ Martin Smith ce serait surprenant (et éducatif pour moi) si cela fait une différence. –

Répondre

16

Toute différence si vous changez le IN à =?

Si quelqu'un veut approfondir cette question, je viens de faire un test et je l'ai trouvé très facile à reproduire.

Create Table

CREATE TABLE `filler` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`) 
) 

Create Procedure

CREATE PROCEDURE `prc_filler`(cnt INT) 
BEGIN 
     DECLARE _cnt INT; 
     SET _cnt = 1; 
     WHILE _cnt <= cnt DO 
       INSERT 
       INTO filler 
       SELECT _cnt; 
       SET _cnt = _cnt + 1; 
     END WHILE; 
END 

Populate Tableau

call prc_filler(5000) 

Query 1

SELECT id 
FROM filler 
WHERE id = (SELECT MAX(id) FROM filler WHERE id = 
(SELECT MIN(id) 
    FROM filler 
    WHERE id between 2000 and 3000 
    ) 
) 

Equals Explain Output http://img689.imageshack.us/img689/5592/equals.png

Requête 2 (même problème)

SELECT id 
FROM filler 
WHERE id in (SELECT MAX(id) FROM filler WHERE id in 
(SELECT MIN(id) 
    FROM filler 
    WHERE id between 2000 and 3000 
    ) 
) 

In Explain Output http://img291.imageshack.us/img291/8129/52037513.png

+0

Je serais curieux de voir des experts de MySQL commenter la différence de performance. Il est clair que les sous-requêtes vont retourner 1 ligne seulement alors equals est l'opérateur de droite, mais pourquoi utiliser IN ferait une telle différence de performance quand il vérifie seulement si la valeur est trouvée dans un resultset à 1 rangée? – wadesworld

+3

@Wade - La réponse de Mark ici a ce couvert je pense http://stackoverflow.com/questions/3417074/why-would-an-in-condition-be-slower-than-in-sql/3417190#3417190 –

12

Here is a good explanation why = is better than IN

Mysql a des problèmes avec des requêtes internes - pas bien à l'aide de l'index (le cas échéant).

  1. Assurez-vous que vous avez des index sur tous les champs de la jointure/où/commande, etc.
  2. obtenir ces valeurs MIN et Max dans une requête séparée (utilisez la procédure stockée pour toute cette chose si vous voulez sauter les demandes multiples frais généraux Ou tout simplement faire une demande avec plusieurs requêtes

Quoi qu'il en soit:.

SELECT 
     IdLawVersionValidFrom 
FROM 
     question_law_version 
    JOIN 
     question_law 
     ON 
     question_law_version.IdQuestionLaw = question_law.IdQuestionLaw 
WHERE 
     question_law.IdQuestion=236 
    AND 
     question_law.IdQuestionLaw>63 

ORDER BY 
     IdQuestionLawVersion DESC, 
     question_law.IdQuestionLaw ASC 
LIMIT 1 
+0

+1 Je voulais mentionner les index, mais ... en quelque sorte ... n'a pas :) – Unreason

+0

En fait, le commentaire de Martin Smith est la bonne réponse. –

+0

@Richard Knop - Est-ce que ma requête a fonctionné pour vous ou pas? –

4

Vous pouvez utiliser EXPLAIN pour savoir comment est-il possible pour un requête à exécuter si lente. MySQL n'aime pas vraiment les sous-sélections imbriquées. Donc, ce qui se passe, c'est que ça marche et que l'on fait des tris sur le disque pour obtenir min et max et ne pas réutiliser les résultats. Réécrire en tant que jointures l'aidera probablement.

Si à la recherche d'un essai de solution rapide: (! Posting mes commentaires comme une réponse que semble-t-il fait une différence)

SET @temp1 =  
    (
    SELECT MIN(IdQuestionLaw) 
    FROM question_law 
    WHERE IdQuestion = 236 AND IdQuestionLaw > 63 
) 

SET @temp2 = 
    (
    SELECT MAX(IdQuestionLawVersion) 
    FROM question_law_version 
    WHERE IdQuestionLaw = @temp1 
) 

SELECT IdLawVersionValidFrom 
FROM question_law_version 
WHERE IdQuestionLawVersion = @temp2 
+0

En fait, le commentaire de Martin Smith est la bonne réponse. –

+0

Entièrement d'accord et a donné +1 à cela. Remarquez son utilisation de 'EXPLAIN' :) – Unreason

Questions connexes