Performances MYSQL OR vs IN

180

Je me demande s'il y a une différence en termes de performances entre les éléments 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 optimiseront le code?

EDIT: Changé le AND's en OR' s pour la raison indiquée dans les commentaires.

Scott
la source
Je recherche également cette chose, mais en opposition pour certaines déclarations, IN sera converti en ligne de OR, s I could say that it can also be converted to UNIONce qui est recommandé pour remplacer OR pour optimiser la requête.
Jānis Gruzis

Réponses:

249

J'avais besoin de le savoir avec certitude, alors j'ai évalué les deux méthodes. J'ai toujours trouvé INbeaucoup plus rapide que l'utilisation OR.

Ne croyez pas les gens qui donnent leur "opinion", la science est une question de tests et de preuves.

J'ai exécuté une boucle de 1000x les requêtes équivalentes (par souci de 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 d'origine, comme il y a 6 ans, bien qu'il renvoie un résultat dans la même plage que ce test)

Dans la demande d'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 exécute la même chose.

$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

Cyril
la source
21
Quels index ont été utilisés dans ces tests?
eggyal
5
J'optimisais également les requêtes et j'ai découvert que la INdéclaration était environ 30% plus rapide qu'un fichier OR.
Timo002
12
Do not believe people who give their "opinion"Vous avez 100% raison, Stack Overflow en est malheureusement plein
elipoultorak
7
Raison de la performance (citant la documentation MariaDB (une nouvelle branche gratuite de MySQL)): => si votre colonne est un entier, passez également des entiers à ...Returns 1 if expr is equal to any of the values in the IN list, else returns 0. If all values are constants, they are evaluated according to the type of expr and sorted. The search for the item then is done using a binary search. This means IN is very quick if the IN value list consists entirely of constants . Otherwise, type conversion takes place according to the rules described at Type Conversion, but applied to all the arguments.IN
jave.web
10
Comme corollaire de « Ne croyez pas les gens qui donnent leur« opinion » »: fournir des chiffres de performance sans inclure les scripts, tableaux et index utilisés pour obtenir ces chiffres les rend invérifiables. En tant que tels, les chiffres sont aussi bons qu'une «opinion».
Déçu
67

J'ai également fait un test pour les futurs Googleurs. Le nombre total de résultats renvoyés est de 7264 sur 10000

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

Cette requête a pris 0.1239quelques secondes

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

Cette requête a pris 0.0433quelques secondes

IN est 3 fois plus rapide que OR

Ergec
la source
15
De quel moteur MySQL s'agissait-il et avez-vous effacé les tampons MySQL et les caches de fichiers OS entre les deux requêtes?
dabest1
2
Votre test est un cas d'utilisation restreint. La requête renvoie 72% des données et ne bénéficiera probablement pas des index.
Désillusionné
Je parie que la majeure partie de ce temps a été de consommer la requête, de l'analyser et de la planifier. C'est certainement une considération: si vous allez avoir des instructions OR 10k, vous allez avoir beaucoup de texte redondant pour l'exprimer simplement avec OR: mieux vaut utiliser l'expression la plus compacte possible.
évêque
18

La réponse acceptée n'explique pas la raison.

Ci-dessous sont cités de High Performance MySQL, 3e édition.

Dans de nombreux serveurs de base de données, IN () n'est qu'un synonyme de plusieurs clauses OR, car les deux sont logiquement équivalentes. Ce n'est pas le cas dans MySQL, qui trie les valeurs de la liste IN () et utilise une recherche binaire rapide pour voir si une valeur est dans la liste. C'est O (Log n) dans la taille de la liste, alors qu'une série équivalente de clauses OR est O (n) dans la taille de la liste (c'est-à-dire beaucoup plus lent pour les grandes listes)

Jacob
la source
Référence fantastique à la raison spécifique de la base de données. Agréable!
Joshua Pinter
Parfait et au point
gaurav9620
16

Je pense que le BETWEEN sera plus rapide puisqu'il devrait être converti en:

Field >= 0 AND Field <= 5

Je crois comprendre qu'un IN sera converti de toute façon en un tas d'instructions OR. La valeur de IN est la facilité d'utilisation. (Vous évitez d'avoir à taper chaque nom de colonne plusieurs fois et cela facilite également l'utilisation avec la logique existante - vous n'avez pas à vous soucier de la priorité AND / OR car IN est une instruction. Avec un tas d'instructions OR, vous avez pour vous assurer de 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 QUESTIONS . Vous saurez alors ce qui fonctionne le mieux dans votre situation particulière.

plage
la source
Statistiquement, Between a une chance de déclencher l'index de plage. IN () n'a pas ce privilège. Mais oui, beach a raison: vous DEVEZ profiler votre demande pour savoir si un index est utilisé et lequel. Il est vraiment difficile de prédire ce que l'optimiseur MySQL va choisir.
Savageman
"Je crois comprendre qu'un IN sera de toute façon converti en un tas d'instructions OR." Où avez-vous lu ceci? Je m'attendrais à ce qu'il le mette dans un hashmap pour faire des recherches O (1).
Ztyx
IN est converti en OR est la façon dont SQLServer le gère (ou du moins il l'a fait - pourrait avoir changé maintenant, ne pas l'utiliser depuis des années). Je n'ai trouvé aucune preuve que MySQL fait cela.
RichardAtHome
4
Cette réponse est correcte, entre est converti en "1 <= film_id <= 5". Les deux autres solutions ne sont pas regroupées en une seule condition de plage. J'ai un article de blog qui le démontre en utilisant OPTIMIZER TRACE ici: tocker.ca/2015/05/25/…
Morgan Tocker
13

Cela dépend de ce que vous faites; quelle est la largeur 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'une instance dans laquelle vous souhaitez écrire la requête dans les deux sens; faites-le fonctionner, puis utilisez EXPLAIN pour déterminer les différences d'exécution.

Je suis sûr qu'il y a une réponse concrète à cela, mais c'est ainsi que je pourrais, en pratique, trouver la réponse à ma question donnée.

Cela peut être utile: http://forge.mysql.com/wiki/Top10SQLPerformanceTips

Cordialement,
Frank

Frank V
la source
2
Cela devrait être la réponse choisie.
Jon z
3
Le lien est périmé - je pense que cela peut être l'équivalent? wikis.oracle.com/pages/viewpage.action?pageId=27263381 (merci Oracle ;-P)
ilasno
1
Sur la page équivalente, il est dit: "Évitez d'utiliser IN (...) lors de la sélection sur des champs indexés, cela tuera les performances de la requête SELECT." - Une idée de pourquoi?
jorisw
l'url a expiré
Steve Jiang
7

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

user658991
la source
4

Juste au moment où vous pensiez que c'était sûr ...

Quelle est votre valeur eq_range_index_dive_limit? En particulier, avez-vous plus ou moins d'articles dans l' INarticle?

Cela n'inclura pas de benchmark, mais examinera un peu le fonctionnement interne. Utilisons un outil pour voir ce qui se passe - Optimizer Trace.

La requête: SELECT * FROM canada WHERE id ...

Avec une valeur ORde 3 , une partie de la trace ressemble à:

       "condition_processing": {
          "condition": "WHERE",
          "original_condition": "((`canada`.`id` = 296172) or (`canada`.`id` = 295093) or (`canada`.`id` = 293626))",
          "steps": [
            {
              "transformation": "equality_propagation",
              "resulting_condition": "(multiple equal(296172, `canada`.`id`) or multiple equal(295093, `canada`.`id`) or multiple equal(293626, `canada`.`id`))"
            },

...

              "analyzing_range_alternatives": {
                "range_scan_alternatives": [
                  {
                    "index": "id",
                    "ranges": [
                      "293626 <= id <= 293626",
                      "295093 <= id <= 295093",
                      "296172 <= id <= 296172"
                    ],
                    "index_dives_for_eq_ranges": true,
                    "chosen": true

...

        "refine_plan": [
          {
            "table": "`canada`",
            "pushed_index_condition": "((`canada`.`id` = 296172) or (`canada`.`id` = 295093) or (`canada`.`id` = 293626))",
            "table_condition_attached": null,
            "access_type": "range"
          }
        ]

Notez comment ICP est donné ORs. Cela implique que cela ORn'est pas transformé en IN, et InnoDB effectuera un tas de =tests via ICP. (Je ne pense pas que cela vaille la peine de considérer MyISAM.)

(Ceci est le 5.6.22-71.0-log de Percona; idest un index secondaire.)

Maintenant pour IN () avec quelques valeurs

eq_range_index_dive_limit= 10; il y a 8 valeurs.

        "condition_processing": {
          "condition": "WHERE",
          "original_condition": "(`canada`.`id` in (296172,295093,293626,295573,297148,296127,295588,295810))",
          "steps": [
            {
              "transformation": "equality_propagation",
              "resulting_condition": "(`canada`.`id` in (296172,295093,293626,295573,297148,296127,295588,295810))"
            },

...

              "analyzing_range_alternatives": {
                "range_scan_alternatives": [
                  {
                    "index": "id",
                    "ranges": [
                      "293626 <= id <= 293626",
                      "295093 <= id <= 295093",
                      "295573 <= id <= 295573",
                      "295588 <= id <= 295588",
                      "295810 <= id <= 295810",
                      "296127 <= id <= 296127",
                      "296172 <= id <= 296172",
                      "297148 <= id <= 297148"
                    ],
                    "index_dives_for_eq_ranges": true,
                    "chosen": true

...

        "refine_plan": [
          {
            "table": "`canada`",
            "pushed_index_condition": "(`canada`.`id` in (296172,295093,293626,295573,297148,296127,295588,295810))",
            "table_condition_attached": null,
            "access_type": "range"
          }
        ]

Notez que le INne semble pas être transformé en OR.

Remarque: notez que les valeurs constantes ont été triées . Cela peut être bénéfique de deux manières:

  • En sautant moins, il peut y avoir une meilleure mise en cache, moins d'E / S pour accéder à toutes les valeurs.
  • Si deux requêtes similaires proviennent de connexions séparées et qu'elles sont dans des transactions, il y a de meilleures chances d'obtenir un retard au lieu d'un blocage en raison du chevauchement des listes.

Enfin, IN () avec beaucoup de valeurs

      {
        "condition_processing": {
          "condition": "WHERE",
          "original_condition": "(`canada`.`id` in (293831,292259,292881,293440,292558,295792,292293,292593,294337,295430,295034,297060,293811,295587,294651,295559,293213,295742,292605,296018,294529,296711,293919,294732,294689,295540,293000,296916,294433,297112,293815,292522,296816,293320,293232,295369,291894,293700,291839,293049,292738,294895,294473,294023,294173,293019,291976,294923,294797,296958,294075,293450,296952,297185,295351,295736,296312,294330,292717,294638,294713,297176,295896,295137,296573,292236,294966,296642,296073,295903,293057,294628,292639,293803,294470,295353,297196,291752,296118,296964,296185,295338,295956,296064,295039,297201,297136,295206,295986,292172,294803,294480,294706,296975,296604,294493,293181,292526,293354,292374,292344,293744,294165,295082,296203,291918,295211,294289,294877,293120,295387))",
          "steps": [
            {
              "transformation": "equality_propagation",
              "resulting_condition": "(`canada`.`id` in (293831,292259,292881,293440,292558,295792,292293,292593,294337,295430,295034,297060,293811,295587,294651,295559,293213,295742,292605,296018,294529,296711,293919,294732,294689,295540,293000,296916,294433,297112,293815,292522,296816,293320,293232,295369,291894,293700,291839,293049,292738,294895,294473,294023,294173,293019,291976,294923,294797,296958,294075,293450,296952,297185,295351,295736,296312,294330,292717,294638,294713,297176,295896,295137,296573,292236,294966,296642,296073,295903,293057,294628,292639,293803,294470,295353,297196,291752,296118,296964,296185,295338,295956,296064,295039,297201,297136,295206,295986,292172,294803,294480,294706,296975,296604,294493,293181,292526,293354,292374,292344,293744,294165,295082,296203,291918,295211,294289,294877,293120,295387))"
            },

...

              "analyzing_range_alternatives": {
                "range_scan_alternatives": [
                  {
                    "index": "id",
                    "ranges": [
                      "291752 <= id <= 291752",
                      "291839 <= id <= 291839",
                      ...
                      "297196 <= id <= 297196",
                      "297201 <= id <= 297201"
                    ],
                    "index_dives_for_eq_ranges": false,
                    "rows": 111,
                    "chosen": true

...

        "refine_plan": [
          {
            "table": "`canada`",
            "pushed_index_condition": "(`canada`.`id` in (293831,292259,292881,293440,292558,295792,292293,292593,294337,295430,295034,297060,293811,295587,294651,295559,293213,295742,292605,296018,294529,296711,293919,294732,294689,295540,293000,296916,294433,297112,293815,292522,296816,293320,293232,295369,291894,293700,291839,293049,292738,294895,294473,294023,294173,293019,291976,294923,294797,296958,294075,293450,296952,297185,295351,295736,296312,294330,292717,294638,294713,297176,295896,295137,296573,292236,294966,296642,296073,295903,293057,294628,292639,293803,294470,295353,297196,291752,296118,296964,296185,295338,295956,296064,295039,297201,297136,295206,295986,292172,294803,294480,294706,296975,296604,294493,293181,292526,293354,292374,292344,293744,294165,295082,296203,291918,295211,294289,294877,293120,295387))",
            "table_condition_attached": null,
            "access_type": "range"
          }
        ]

Note latérale: J'en avais besoin en raison de l'encombrement de la trace:

@@global.optimizer_trace_max_mem_size = 32222;
Rick James
la source
3

OU sera le plus lent. Que IN ou BETWEEN soit plus rapide dépendra de vos données, mais je m'attendrais à ce que BETWEEN soit plus rapide normalement car il peut simplement prendre une plage à partir d'un index (en supposant que someField est indexé).

Greg
la source
3

Vous trouverez ci-dessous les détails de 6 requêtes utilisant MySQL 5.6 @SQLFiddle

En résumé, les 6 requêtes couvrent des colonnes indexées indépendamment et 2 requêtes ont été utilisées par type de données. Toutes les requêtes ont abouti à l'utilisation d'un index indépendamment de IN () ou ORs utilisés.

        |   ORs      |   IN()
integer | uses index | uses index
date    | uses index | uses index
varchar | uses index | uses index

Je voulais vraiment juste démystifier les déclarations faites que OU signifie qu'aucun index ne peut être utilisé. Ce n'est pas vrai. Les index peuvent être utilisés dans les requêtes en utilisant OR comme les 6 requêtes dans les exemples suivants s'affichent.

De plus, il me semble que beaucoup ont ignoré le fait que IN () est un raccourci syntaxique pour un ensemble de OU. À petite échelle, les différences de performances entre l'utilisation de IN () -v- OR sont extrêmement (en fait) marginales.

Alors qu'à plus grande échelle, IN () est certainement plus pratique, mais cela équivaut toujours à un ensemble de conditions OU logiquement. Changement de circonstance pour chaque requête, il est donc toujours préférable de tester votre requête sur vos tables.

Résumé des 6 plans d'explication, tous "Utilisation de la condition d'index" (défilement vers la droite)

  Query               select_type    table    type    possible_keys      key      key_len   ref   rows   filtered           Extra          
                      ------------- --------- ------- --------------- ----------- --------- ----- ------ ---------- ----------------------- 
  Integers using OR   SIMPLE        mytable   range   aNum_idx        aNum_idx    4               10     100.00     Using index condition  
  Integers using IN   SIMPLE        mytable   range   aNum_idx        aNum_idx    4               10     100.00     Using index condition  
  Dates using OR      SIMPLE        mytable   range   aDate_idx       aDate_idx   6               7      100.00     Using index condition  
  Dates using IN      SIMPLE        mytable   range   aDate_idx       aDate_idx   6               7      100.00     Using index condition  
  Varchar using OR    SIMPLE        mytable   range   aName_idx       aName_idx   768             10     100.00     Using index condition  
  Varchar using IN    SIMPLE        mytable   range   aName_idx       aName_idx   768             10     100.00     Using index condition  

Violon SQL

Configuration du schéma MySQL 5.6 :

CREATE TABLE `myTable` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `aName` varchar(255) default NULL,
  `aDate` datetime,
  `aNum`  mediumint(8),
  PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;

ALTER TABLE `myTable` ADD INDEX `aName_idx` (`aName`);
ALTER TABLE `myTable` ADD INDEX `aDate_idx` (`aDate`);
ALTER TABLE `myTable` ADD INDEX `aNum_idx` (`aNum`);

INSERT INTO `myTable` (`aName`,`aDate`)
 VALUES 
 ("Daniel","2017-09-19 01:22:31")
,("Quentin","2017-06-03 01:06:45")
,("Chester","2017-06-14 17:49:36")
,("Lev","2017-08-30 06:27:59")
,("Garrett","2018-10-04 02:40:37")
,("Lane","2017-01-22 17:11:21")
,("Chaim","2017-09-20 11:13:46")
,("Kieran","2018-03-10 18:37:26")
,("Cedric","2017-05-20 16:25:10")
,("Conan","2018-07-10 06:29:39")
,("Rudyard","2017-07-14 00:04:00")
,("Chadwick","2018-08-18 08:54:08")
,("Darius","2018-10-02 06:55:56")
,("Joseph","2017-06-19 13:20:33")
,("Wayne","2017-04-02 23:20:25")
,("Hall","2017-10-13 00:17:24")
,("Craig","2016-12-04 08:15:22")
,("Keane","2018-03-12 04:21:46")
,("Russell","2017-07-14 17:21:58")
,("Seth","2018-07-25 05:51:30")
,("Cole","2018-06-09 15:32:53")
,("Donovan","2017-08-12 05:21:35")
,("Damon","2017-06-27 03:44:19")
,("Brian","2017-02-01 23:35:20")
,("Harper","2017-08-25 04:29:27")
,("Chandler","2017-09-30 23:54:06")
,("Edward","2018-07-30 12:18:07")
,("Curran","2018-05-23 09:31:53")
,("Uriel","2017-05-08 03:31:43")
,("Honorato","2018-04-07 14:57:53")
,("Griffin","2017-01-07 23:35:31")
,("Hasad","2017-05-15 05:32:41")
,("Burke","2017-07-04 01:11:19")
,("Hyatt","2017-03-14 17:12:28")
,("Brenden","2017-10-17 05:16:14")
,("Ryan","2018-10-10 08:07:55")
,("Giacomo","2018-10-06 14:21:21")
,("James","2018-02-06 02:45:59")
,("Colt","2017-10-10 08:11:26")
,("Kermit","2017-09-18 16:57:16")
,("Drake","2018-05-20 22:08:36")
,("Berk","2017-04-16 17:39:32")
,("Alan","2018-09-01 05:33:05")
,("Deacon","2017-04-20 07:03:05")
,("Omar","2018-03-02 15:04:32")
,("Thaddeus","2017-09-19 04:07:54")
,("Troy","2016-12-13 04:24:08")
,("Rogan","2017-11-02 00:03:25")
,("Grant","2017-08-21 01:45:16")
,("Walker","2016-11-26 15:54:52")
,("Clarke","2017-07-20 02:26:56")
,("Clayton","2018-08-16 05:09:29")
,("Denton","2018-08-11 05:26:05")
,("Nicholas","2018-07-19 09:29:55")
,("Hashim","2018-08-10 20:38:06")
,("Todd","2016-10-25 01:01:36")
,("Xenos","2017-05-11 22:50:35")
,("Bert","2017-06-17 18:08:21")
,("Oleg","2018-01-03 13:10:32")
,("Hall","2018-06-04 01:53:45")
,("Evan","2017-01-16 01:04:25")
,("Mohammad","2016-11-18 05:42:52")
,("Armand","2016-12-18 06:57:57")
,("Kaseem","2018-06-12 23:09:57")
,("Colin","2017-06-29 05:25:52")
,("Arthur","2016-12-29 04:38:13")
,("Xander","2016-11-14 19:35:32")
,("Dante","2016-12-01 09:01:04")
,("Zahir","2018-02-17 14:44:53")
,("Raymond","2017-03-09 05:33:06")
,("Giacomo","2017-04-17 06:12:52")
,("Fulton","2017-06-04 00:41:57")
,("Chase","2018-01-14 03:03:57")
,("William","2017-05-08 09:44:59")
,("Fuller","2017-03-31 20:35:20")
,("Jarrod","2017-02-15 02:45:29")
,("Nissim","2018-03-11 14:19:25")
,("Chester","2017-11-05 00:14:27")
,("Perry","2017-12-24 11:58:04")
,("Theodore","2017-06-26 12:34:12")
,("Mason","2017-10-02 03:53:49")
,("Brenden","2018-10-08 10:09:47")
,("Jerome","2017-11-05 20:34:25")
,("Keaton","2018-08-18 00:55:56")
,("Tiger","2017-05-21 16:59:07")
,("Benjamin","2018-04-10 14:46:36")
,("John","2018-09-05 18:53:03")
,("Jakeem","2018-10-11 00:17:38")
,("Kenyon","2017-12-18 22:19:29")
,("Ferris","2017-03-29 06:59:13")
,("Hoyt","2017-01-03 03:48:56")
,("Fitzgerald","2017-07-27 11:27:52")
,("Forrest","2017-10-05 23:14:21")
,("Jordan","2017-01-11 03:48:09")
,("Lev","2017-05-25 08:03:39")
,("Chase","2017-06-18 19:09:23")
,("Ryder","2016-12-13 12:50:50")
,("Malik","2017-11-19 15:15:55")
,("Zeph","2018-04-04 11:22:12")
,("Amala","2017-01-29 07:52:17")
;

.

update MyTable
set aNum = id
;

Requête 1 :

select 'aNum by OR' q, mytable.*
from mytable
where aNum = 12
OR aNum = 22
OR aNum = 27
OR aNum = 32
OR aNum = 42
OR aNum = 52
OR aNum = 62
OR aNum = 65
OR aNum = 72
OR aNum = 82

Résultats :

|          q | id |    aName |                aDate | aNum |
|------------|----|----------|----------------------|------|
| aNum by OR | 12 | Chadwick | 2018-08-18T08:54:08Z |   12 |
| aNum by OR | 22 |  Donovan | 2017-08-12T05:21:35Z |   22 |
| aNum by OR | 27 |   Edward | 2018-07-30T12:18:07Z |   27 |
| aNum by OR | 32 |    Hasad | 2017-05-15T05:32:41Z |   32 |
| aNum by OR | 42 |     Berk | 2017-04-16T17:39:32Z |   42 |
| aNum by OR | 52 |  Clayton | 2018-08-16T05:09:29Z |   52 |
| aNum by OR | 62 | Mohammad | 2016-11-18T05:42:52Z |   62 |
| aNum by OR | 65 |    Colin | 2017-06-29T05:25:52Z |   65 |
| aNum by OR | 72 |   Fulton | 2017-06-04T00:41:57Z |   72 |
| aNum by OR | 82 |  Brenden | 2018-10-08T10:09:47Z |   82 |

Requête 2 :

select 'aNum by IN' q, mytable.*
from mytable
where aNum IN (
            12
          , 22
          , 27
          , 32
          , 42
          , 52
          , 62
          , 65
          , 72
          , 82
          )

Résultats :

|          q | id |    aName |                aDate | aNum |
|------------|----|----------|----------------------|------|
| aNum by IN | 12 | Chadwick | 2018-08-18T08:54:08Z |   12 |
| aNum by IN | 22 |  Donovan | 2017-08-12T05:21:35Z |   22 |
| aNum by IN | 27 |   Edward | 2018-07-30T12:18:07Z |   27 |
| aNum by IN | 32 |    Hasad | 2017-05-15T05:32:41Z |   32 |
| aNum by IN | 42 |     Berk | 2017-04-16T17:39:32Z |   42 |
| aNum by IN | 52 |  Clayton | 2018-08-16T05:09:29Z |   52 |
| aNum by IN | 62 | Mohammad | 2016-11-18T05:42:52Z |   62 |
| aNum by IN | 65 |    Colin | 2017-06-29T05:25:52Z |   65 |
| aNum by IN | 72 |   Fulton | 2017-06-04T00:41:57Z |   72 |
| aNum by IN | 82 |  Brenden | 2018-10-08T10:09:47Z |   82 |

Requête 3 :

select 'adate by OR' q, mytable.*
from mytable
where aDate= str_to_date("2017-02-15 02:45:29",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2018-03-10 18:37:26",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2017-05-20 16:25:10",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2018-07-10 06:29:39",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2017-07-14 00:04:00",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2018-08-18 08:54:08",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2018-10-02 06:55:56",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2017-04-20 07:03:05",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2018-03-02 15:04:32",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2017-09-19 04:07:54",'%Y-%m-%d %h:%i:%s')
OR aDate = str_to_date("2016-12-13 04:24:08",'%Y-%m-%d %h:%i:%s')

Résultats :

|           q | id |    aName |                aDate | aNum |
|-------------|----|----------|----------------------|------|
| adate by OR | 47 |     Troy | 2016-12-13T04:24:08Z |   47 |
| adate by OR | 76 |   Jarrod | 2017-02-15T02:45:29Z |   76 |
| adate by OR | 44 |   Deacon | 2017-04-20T07:03:05Z |   44 |
| adate by OR | 46 | Thaddeus | 2017-09-19T04:07:54Z |   46 |
| adate by OR | 10 |    Conan | 2018-07-10T06:29:39Z |   10 |
| adate by OR | 12 | Chadwick | 2018-08-18T08:54:08Z |   12 |
| adate by OR | 13 |   Darius | 2018-10-02T06:55:56Z |   13 |

Requête 4 :

select 'adate by IN' q, mytable.*
from mytable
where aDate IN (
          str_to_date("2017-02-15 02:45:29",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2018-03-10 18:37:26",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2017-05-20 16:25:10",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2018-07-10 06:29:39",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2017-07-14 00:04:00",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2018-08-18 08:54:08",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2018-10-02 06:55:56",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2017-04-20 07:03:05",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2018-03-02 15:04:32",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2017-09-19 04:07:54",'%Y-%m-%d %h:%i:%s')
        , str_to_date("2016-12-13 04:24:08",'%Y-%m-%d %h:%i:%s')
        )

Résultats :

|           q | id |    aName |                aDate | aNum |
|-------------|----|----------|----------------------|------|
| adate by IN | 47 |     Troy | 2016-12-13T04:24:08Z |   47 |
| adate by IN | 76 |   Jarrod | 2017-02-15T02:45:29Z |   76 |
| adate by IN | 44 |   Deacon | 2017-04-20T07:03:05Z |   44 |
| adate by IN | 46 | Thaddeus | 2017-09-19T04:07:54Z |   46 |
| adate by IN | 10 |    Conan | 2018-07-10T06:29:39Z |   10 |
| adate by IN | 12 | Chadwick | 2018-08-18T08:54:08Z |   12 |
| adate by IN | 13 |   Darius | 2018-10-02T06:55:56Z |   13 |

Requête 5 :

select 'name by  OR' q, mytable.*
from mytable
where aname = 'Alan'
OR aname = 'Brian'
OR aname = 'Chandler'
OR aname = 'Darius'
OR aname = 'Evan'
OR aname = 'Ferris'
OR aname = 'Giacomo'
OR aname = 'Hall'
OR aname = 'James'
OR aname = 'Jarrod'

Résultats :

|           q | id |    aName |                aDate | aNum |
|-------------|----|----------|----------------------|------|
| name by  OR | 43 |     Alan | 2018-09-01T05:33:05Z |   43 |
| name by  OR | 24 |    Brian | 2017-02-01T23:35:20Z |   24 |
| name by  OR | 26 | Chandler | 2017-09-30T23:54:06Z |   26 |
| name by  OR | 13 |   Darius | 2018-10-02T06:55:56Z |   13 |
| name by  OR | 61 |     Evan | 2017-01-16T01:04:25Z |   61 |
| name by  OR | 90 |   Ferris | 2017-03-29T06:59:13Z |   90 |
| name by  OR | 37 |  Giacomo | 2018-10-06T14:21:21Z |   37 |
| name by  OR | 71 |  Giacomo | 2017-04-17T06:12:52Z |   71 |
| name by  OR | 16 |     Hall | 2017-10-13T00:17:24Z |   16 |
| name by  OR | 60 |     Hall | 2018-06-04T01:53:45Z |   60 |
| name by  OR | 38 |    James | 2018-02-06T02:45:59Z |   38 |
| name by  OR | 76 |   Jarrod | 2017-02-15T02:45:29Z |   76 |

Requête 6 :

select 'name by IN' q, mytable.*
from mytable
where aname IN (
      'Alan'
     ,'Brian'
     ,'Chandler'
     , 'Darius'
     , 'Evan'
     , 'Ferris'
     , 'Giacomo'
     , 'Hall'
     , 'James'
     , 'Jarrod'
     )

Résultats :

|          q | id |    aName |                aDate | aNum |
|------------|----|----------|----------------------|------|
| name by IN | 43 |     Alan | 2018-09-01T05:33:05Z |   43 |
| name by IN | 24 |    Brian | 2017-02-01T23:35:20Z |   24 |
| name by IN | 26 | Chandler | 2017-09-30T23:54:06Z |   26 |
| name by IN | 13 |   Darius | 2018-10-02T06:55:56Z |   13 |
| name by IN | 61 |     Evan | 2017-01-16T01:04:25Z |   61 |
| name by IN | 90 |   Ferris | 2017-03-29T06:59:13Z |   90 |
| name by IN | 37 |  Giacomo | 2018-10-06T14:21:21Z |   37 |
| name by IN | 71 |  Giacomo | 2017-04-17T06:12:52Z |   71 |
| name by IN | 16 |     Hall | 2017-10-13T00:17:24Z |   16 |
| name by IN | 60 |     Hall | 2018-06-04T01:53:45Z |   60 |
| name by IN | 38 |    James | 2018-02-06T02:45:59Z |   38 |
| name by IN | 76 |   Jarrod | 2017-02-15T02:45:29Z |   76 |
Used_By_Already
la source
2

Je parie que ce sont les mêmes, vous pouvez exécuter un test en procédant comme suit:

bouclez 500 fois le "in (1,2,3,4)" et voyez combien de temps cela prend. boucle sur la version "= 1 ou = 2 ou = 3 ..." 500 fois et voir combien de temps il s'exécute.

vous pouvez également essayer une méthode de jointure, si someField est un index et que votre table est grande, cela pourrait être plus rapide ...

SELECT ... 
    FROM ... 
        INNER JOIN (SELECT 1 as newField UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) dt ON someFIELD =newField

J'ai essayé la méthode de jointure ci-dessus sur mon serveur SQL et elle est presque la même que dans (1, 2, 3, 4), et elles résultent toutes deux en une recherche d'index groupé. Je ne sais pas comment MySQL va les gérer.

KM.
la source
2

2018 : IN (...) est plus rapide. Mais > = && <= est encore plus rapide que IN .

Voici ma référence .

malReiko
la source
0

D'après ce que je comprends de la façon dont le compilateur optimise ces types de requêtes, l'utilisation de la clause IN est plus efficace que plusieurs clauses OR. Si vous avez des valeurs où la clause BETWEEN peut être utilisée, c'est encore plus efficace.

Bois Brandon
la source
0

Je sais que, tant que vous avez un index sur Field, le BETWEEN l'utilisera pour trouver rapidement une extrémité, puis traversera vers l'autre. C'est le plus efficace.

Chaque EXPLAIN que j'ai vu montre que "IN (...)" et "... OR ..." sont interchangeables et tout aussi (in) efficaces. Ce à quoi vous vous attendez, car l'optimiseur n'a aucun moyen de savoir s'ils comprennent ou non un intervalle. Cela équivaut également à UNION ALL SELECT sur les valeurs individuelles.

dkretz
la source
0

Comme expliqué par d'autres, IN est mieux choisi que OR en ce qui concerne les performances des requêtes.

Les requêtes avec une condition OR peuvent prendre plus de temps d'exécution dans les cas ci-dessous.

  1. à exécuter si l'optimiseur MySQL choisit un autre index pour être efficace (en cas de faux positifs).
  2. Si le nombre d'enregistrements est supérieur (comme clairement indiqué par Jacob)
Adithya
la source