203 votes

Performances de MYSQL OR vs IN

Je me demande s'il existe une différence en termes de performances entre les produits suivants

SELECT ... FROM ... WHERE someFIELD IN(1,2,3,4)

SELECT ... FROM ... WHERE someFIELD between  0 AND 5

SELECT ... FROM ... WHERE someFIELD = 1 OR someFIELD = 2 OR someFIELD = 3 ... 

ou MySQL optimisera-t-il le SQL de la même manière que les compilateurs optimisent le code ?

EDIT : Changement du AND à OR pour la raison indiquée dans les commentaires.

0 votes

Je suis également en train de faire des recherches sur ce sujet, mais je m'oppose à ce que les déclarations IN soient converties en lignes de OR. s I could say that it can also be converted to UNION s qui est recommandé pour remplacer les OR`s afin d'optimiser la requête.

1 votes

Il y a eu quelques changements d'optimisation dans ce domaine, donc certaines des réponses suivantes peuvent être "dépassées".

0 votes

En particulier. Le nombre d'articles mai matière. La façon dont les chiffres sont "agglutinés" mai matière ( BETWEEN 1 AND 4 correspond parfaitement, et mai être plus rapide). La version de MySQL/MariaDB mai matière.

274voto

sunseeker Points 1070

Je devais en avoir le cœur net et j'ai donc testé les deux méthodes. J'ai toujours trouvé IN est beaucoup plus rapide que l'utilisation de OR .

Ne croyez pas les personnes qui donnent leur "opinion", la science est faite de tests et de preuves.

J'ai exécuté une boucle de 1000x les requêtes équivalentes (pour la cohérence, j'ai utilisé sql_no_cache ):

IN : 2.34969592094s

OR : 5.83781504631s

Mise à jour :
(Je n'ai pas le code source du test original, car il date d'il y a 6 ans, mais il renvoie un résultat dans la même fourchette que ce test).

Si je demande un exemple de code pour tester cela, voici le cas d'utilisation le plus simple possible. En utilisant Eloquent pour la simplicité de la syntaxe, l'équivalent SQL brut s'exécute de la même manière.

$t = microtime(true); 
for($i=0; $i<10000; $i++):
$q = DB::table('users')->where('id',1)
    ->orWhere('id',2)
    ->orWhere('id',3)
    ->orWhere('id',4)
    ->orWhere('id',5)
    ->orWhere('id',6)
    ->orWhere('id',7)
    ->orWhere('id',8)
    ->orWhere('id',9)
    ->orWhere('id',10)
    ->orWhere('id',11)
    ->orWhere('id',12)
    ->orWhere('id',13)
    ->orWhere('id',14)
    ->orWhere('id',15)
    ->orWhere('id',16)
    ->orWhere('id',17)
    ->orWhere('id',18)
    ->orWhere('id',19)
    ->orWhere('id',20)->get();
endfor;
$t2 = microtime(true); 
echo $t."\n".$t2."\n".($t2-$t)."\n";

1482080514.3635
1482080517.3713
3.0078368186951

$t = microtime(true); 
for($i=0; $i<10000; $i++): 
$q = DB::table('users')->whereIn('id',[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])->get(); 
endfor; 
$t2 = microtime(true); 
echo $t."\n".$t2."\n".($t2-$t)."\n";

1482080534.0185
1482080536.178
2.1595389842987

22 votes

Quels indices ont été utilisés dans ces tests ?

0 votes

Wow, c'est à la fois génial et nul. MySQL ne cesse de me surprendre avec ce genre de choses.

16 votes

Do not believe people who give their "opinion" Tu as raison à 100%, Stack Overflow en est malheureusement plein.

72voto

Ergec Points 3199

J'ai aussi fait un test pour les futurs Googlers. Le nombre total de résultats retournés est de 7264 sur 10000.

SELECT * FROM item WHERE id = 1 OR id = 2 ... id = 10000

Cette requête a pris 0.1239 secondes

SELECT * FROM item WHERE id IN (1,2,3,...10000)

Cette requête a pris 0.0433 secondes

IN est 3 fois plus rapide que OR

17 votes

Quel était le moteur MySQL et avez-vous vidé les tampons MySQL et les caches des fichiers du système d'exploitation entre les deux requêtes ?

2 votes

Votre test est un cas d'utilisation étroit. La requête renvoie 72% des données, et il est peu probable qu'elle bénéficie d'index.

0 votes

Je parie que la plupart de ce temps a été consacré à la consommation de la requête, à son analyse et à sa planification. C'est certainement un élément à prendre en compte : si vous avez 10 000 instructions OR, vous allez avoir beaucoup de texte redondant en l'exprimant simplement avec OR Il est préférable d'utiliser l'expression la plus compacte possible.

16voto

beach Points 4116

Je pense que l'option ENTRE sera plus rapide puisqu'elle doit être convertie en :

Field >= 0 AND Field <= 5

Je crois savoir qu'un IN sera de toute façon converti en un ensemble d'instructions OR. L'intérêt des IN est leur facilité d'utilisation. (Il évite d'avoir à taper plusieurs fois le nom de chaque colonne et facilite également l'utilisation de la logique existante - vous n'avez pas à vous soucier de la précédence AND/OR car le IN est une seule instruction. Avec un tas d'instructions OR, vous devez veiller à les entourer de parenthèses pour vous assurer qu'elles sont évaluées comme une seule condition).

La seule vraie réponse à votre question est PROFILER VOS REQUÊTES . Vous saurez alors ce qui fonctionne le mieux dans votre situation particulière.

0 votes

Statistiquement, Between a une chance de déclencher l'indice de gamme. IN() n'a pas ce privilège. Mais oui, Beach a raison : vous DEVEZ profiler votre requête pour savoir si un index est utilisé et lequel. Il est vraiment difficile de prédire ce que l'optimiseur MySQL va choisir.

0 votes

"Je crois savoir qu'un IN sera de toute façon converti en un ensemble d'instructions OR." Où avez-vous lu cela ? Je m'attendrais à ce qu'il soit mis dans un hashmap pour faire des recherches O(1).

0 votes

Les IN sont convertis en OR, c'est ainsi que SQLServer le gère (ou du moins il le faisait - cela a peut-être changé maintenant, je ne l'ai pas utilisé depuis des années). Je n'ai pas pu trouver de preuve que MySQL fasse cela.

14voto

Frank V Points 9690

Cela dépend de ce que vous faites ; quelle est l'étendue de la plage, quel est le type de données (je sais que votre exemple utilise un type de données numérique mais votre question peut également s'appliquer à de nombreux types de données différents).

Il s'agit d'un cas où vous voulez écrire la requête dans les deux sens, la faire fonctionner et ensuite utiliser EXPLAIN pour comprendre les différences d'exécution.

Je suis sûr qu'il existe une réponse concrète à cette question, mais c'est ainsi que je trouverais, en pratique, la réponse à ma question.

Cela pourrait vous aider : http://forge.mysql.com/wiki/Top10SQLPerformanceTips

Regards,
Frank

2 votes

Ce devrait être la réponse choisie.

3 votes

Le lien est périmé - je pense que ceci peut être l'équivalent ? wikis.oracle.com/pages/viewpage.action?pageId=27263381 (merci Oracle ;-P)

1 votes

Sur la page équivalente, il est indiqué : "Évitez d'utiliser IN(...) lors de la sélection sur des champs indexés, cela réduira les performances de la requête SELECT". - Une idée de la raison ?

7voto

user658991 Points 477

Je pense qu'une explication à l'observation de sunseeker est que MySQL trie réellement les valeurs dans l'instruction IN si elles sont toutes des valeurs statiques et en utilisant la recherche binaire, qui est plus efficace que l'alternative OR simple. Je ne me souviens pas où j'ai lu cela, mais le résultat de sunseeker semble être une preuve.

0 votes

Moi aussi, j'ai entendu dire que la liste est triée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X