Consultez la question This SELECT query takes 180 seconds to finish (vérifiez les commentaires sur la question elle-même).
L'IN peut être comparé à une seule valeur, mais la différence de temps est énorme.
Pourquoi est-ce comme ça?Pourquoi une condition IN serait-elle plus lente que "=" en sql?
Répondre
Résumé: Ceci est un known problem dans MySQL et a été corrigé dans MySQL 5.6.x. Le problème est dû à une optimisation manquante lorsqu'une sous-requête utilisant IN est incorrectement identifiée comme sous-requête dépendante au lieu d'une sous-requête indépendante.
Lorsque vous exécutez EXPLIQUEZ sur la requête d'origine, il retourne ceci:
1 'PRIMARY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 2 'DEPENDENT SUBQUERY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 3 'DEPENDENT SUBQUERY' 'question_law' 'ALL' '' '' '' '' 10040 'Using where'
Lorsque vous modifiez IN
-=
vous obtenez ceci:
1 'PRIMARY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 2 'SUBQUERY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 3 'SUBQUERY' 'question_law' 'ALL' '' '' '' '' 10040 'Using where'
Chaque sous-requête dépendante est exécutée une fois par ligne dans la requête dans laquelle elle est contenue, alors que la sous-requête est exécutée une seule fois. MySQL peut parfois optimiser les sous-requêtes dépendantes quand il y a une condition qui peut être convertie en une jointure mais ici ce n'est pas le cas. Maintenant, cela laisse bien sûr la question de savoir pourquoi MySQL pense que la version IN doit être une sous-requête dépendante. J'ai fait une version simplifiée de la requête pour aider à étudier cela. J'ai créé deux tables 'foo' et 'bar' où la première contient seulement une colonne id, et la seconde contient à la fois un id et un foo id (bien que je n'ai pas créé une contrainte de clé étrangère). Ensuite, je peuplé les deux tables avec 1000 lignes:
CREATE TABLE foo (id INT PRIMARY KEY NOT NULL);
CREATE TABLE bar (id INT PRIMARY KEY, foo_id INT NOT NULL);
-- populate tables with 1000 rows in each
SELECT id
FROM foo
WHERE id IN
(
SELECT MAX(foo_id)
FROM bar
);
Cette requête simplifiée a le même problème que précédemment - la sélection intérieure est traitée comme une sous-requête dépendante et aucune optimisation est effectuée, ce qui provoque la requête interne à exécuter une fois par rangée. La requête prend presque une seconde à courir. Changer le IN
en =
permet à nouveau à la requête de s'exécuter presque instantanément.
Le code que j'ai utilisé pour remplir les tableaux est ci-dessous, au cas où quelqu'un souhaite reproduire les résultats.
CREATE TABLE filler (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;
DELIMITER $$
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
$$
DELIMITER ;
CALL prc_filler(1000);
INSERT foo SELECT id FROM filler;
INSERT bar SELECT id, id FROM filler;
Existe-t-il un moyen de forcer l'optimiseur à traiter une sous-requête uniquement comme une sous-requête et non comme une sous-requête dépendante? –
@Itay Moav: MySQL devrait être en mesure de déterminer par lui-même quelles sous-requêtes dépendent des requêtes externes. Je suis encore un peu surpris que dans ce cas, il pense que la requête interne est une requête dépendante quand il n'y a clairement aucune référence à la table d'origine. Je pourrais rechercher la base de données de bogues pour voir si n'importe qui a signalé ce problème. –
@Itay Moav: J'ai simplifié la requête et répliqué le même problème sur la requête plus simple. J'ai trouvé un rapport de bogue dans MySQL qui décrit exactement le même problème. Les développeurs MySQL promettent une solution. J'ai mis à jour ma réponse en conséquence. J'espère que cela répond à votre question complètement. PS: +1 pour la bonne question qui m'obligeait à faire des recherches! :) –
Les optimiseurs SQL ne font pas toujours ce que vous attendez d'eux. Je ne suis pas sûr qu'il y ait une meilleure réponse que ça. C'est pourquoi vous devez examiner la sortie EXPLAIN PLAN, et profiler vos requêtes pour savoir où le temps est passé.
pour recommander EXPLAIN comme point de départ pour analyser les performances des requêtes . – Cumbayah
Il s'agit de requêtes internes a.k.a sous-requêtes vs jointures, pas sur IN vs =, et les raisons sont expliquées dans ce post. La version 5.4 de MySQL est prévue pour introduire un optimiseur amélioré, qui peut réécrire certaines sous-requêtes dans une forme plus efficace.
La pire chose que vous pouvez faire, est d'utiliser ce qu'on appelle sous-requête corrélée http://dev.mysql.com/doc/refman/5.1/en/correlated-subqueries.html
Il est intéressant mais le problème peut également être résolu avec les instructions préparées (pas sûr si elle convient à tout le monde), par exemple:
mysql> EXPLAIN SELECT * FROM words WHERE word IN (SELECT word FROM phrase_words);
+----+--------------------+--------------+...
| id | select_type | table |...
+----+--------------------+--------------+...
| 1 | PRIMARY | words |...
| 2 | DEPENDENT SUBQUERY | phrase_words |...
+----+--------------------+--------------+...
mysql> EXPLAIN SELECT * FROM words WHERE word IN ('twist','rollers');
+----+-------------+-------+...
| id | select_type | table |...
+----+-------------+-------+...
| 1 | SIMPLE | words |...
+----+-------------+-------+...
Il suffit donc de préparer la déclaration dans une procédure stockée , puis exécutez-le.Voici l'idée:
SET @words = (SELECT GROUP_CONCAT(word SEPARATOR '\',\'') FROM phrase_words);
SET @words = CONCAT("'", @words, "'");
SET @query = CONCAT("SELECT * FROM words WHERE word IN (", @words, ");";
PREPARE q FROM @query;
EXECUTE q;
Si vous voulez aller à cette déroute, alors créer dans le SP une table temporaire avec seulement les valeurs que vous voulez dans l'IN et le joindre à la table principale. –
C'est un bon point, merci beaucoup! – Maksim
- 1. Pourquoi l'édition SQL Server Express est plus lente que l'édition Web SQL Server?
- 2. Pourquoi une insertion de 1M est-elle plus lente sans transaction que dans une transaction?
- 3. Pourquoi un schéma rendrait-il une colonne XML plus lente?
- 4. SQL: relation plusieurs-à-plusieurs, condition IN
- 5. En python, pourquoi la lecture d'un tableau est-elle plus lente que la lecture d'une liste?
- 6. Datatable.compute lente avec comme condition
- 7. Pourquoi la détection des touches devient-elle plus lente et plus lente?
- 8. Comment définir plus une condition en temps
- 9. Pourquoi la liaison de données Linq To Sql vers gridview est-elle beaucoup plus lente que le SQL direct?
- 10. Like condition in LINQ
- 11. if condition in nant
- 12. Pourquoi la vitesse de dessin graphique est plus lente en C# que VB6
- 13. Pourquoi le code est-il compilé avec l'expression <TDelegate> .Compile() plus lente que C#?
- 14. Pourquoi l'index a-t-il rendu cette requête plus lente?
- 15. Lente sélection dans QTreeView, pourquoi?
- 16. Requête SQL sur une condition
- 17. Pourquoi utiliser une affectation dans une condition?
- 18. L'application Java est plus lente en raison du démarrage Web
- 19. SQL Select Condition Question
- 20. perl, dbi avec une instruction SQL avec une condition similaire
- 21. Comment avoir une condition dans une requête SQL imbriquée?
- 22. HABTM trouver une condition
- 23. Exécution d'une application .NET plus lente
- 24. sous condition SQL sur Bit
- 25. Y a-t-il une raison pour que cette simple requête SQL soit si lente?
- 26. Pourquoi la fonction isprefix est-elle plus rapide que Startswith en C#?
- 27. Pourquoi Oracle Sql * Plus imprime de nombreux en-têtes inutiles?
- 28. Une DLL est-elle plus lente qu'un lien statique?
- 29. SQL: SELECT IN plus rapide et meilleure pratique?
- 30. passage condition booléenne en tant que paramètre
@nos l'OP mentionné que la modification des '' IN's à = 'a réduit le temps de 180 secondes à 1 0.00008s – NullUserException