Wydajność MYSQL OR vs IN


180

Zastanawiam się, czy jest jakaś różnica w wydajności między następującymi

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 ... 

czy MySQL zoptymalizuje SQL w ten sam sposób, w jaki kompilatory zoptymalizują kod?

EDYCJA: Zmieniono AND„na OR” z powodu podanego w komentarzach.


Badam również tę rzecz, ale w przeciwieństwie do niektórych stwierdzeń, że IN zostanie przekonwertowany na wiersz OR, s I could say that it can also be converted to UNIONktóry jest zalecany do zastąpienia OR w celu optymalizacji zapytania.
Jānis Gruzis,

Odpowiedzi:


249

Musiałem to wiedzieć na pewno, więc porównałem obie metody. Konsekwentnie uważam, INże jest znacznie szybszy niż używanie OR.

Nie wierz ludziom, którzy wyrażają swoją „opinię”, nauka polega na testowaniu i dowodach.

Uruchomiłem pętlę 1000x równoważnych zapytań (dla spójności użyłem sql_no_cache):

IN: 2,34969592094s

OR: 5.83781504631s

Aktualizacja:
(Nie mam kodu źródłowego dla oryginalnego testu, tak jak to było 6 lat temu, chociaż zwraca wynik w tym samym zakresie co ten test)

W przypadku prośby o przykładowy kod do przetestowania tego, oto najprostszy możliwy przypadek użycia. Używając Eloquent dla uproszczenia składni, surowy odpowiednik SQL wykonuje to samo.

$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


21
Jakie indeksy zastosowano w tych testach?
eggyal

5
Optymalizowałem również zapytania i odkryłem, że INoświadczenie było około 30% szybsze niż an OR.
Timo002

12
Do not believe people who give their "opinion"Masz 100% racji, Stack Overflow jest niestety pełen
elipoultorak

7
Powód wydajności (cytuje MariaDB (nowy darmowy oddział MySQL)): => jeśli twoja kolumna jest liczbą całkowitą, przekaż liczby całkowite też ...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 14.08.16

10
W następstwie „ Nie wierz ludziom, którzy wyrażają swoją opinię” : Podanie danych liczbowych dotyczących wydajności bez uwzględnienia skryptów, tabel i indeksów użytych do ich uzyskania sprawia, że ​​nie można ich zweryfikować. Jako takie liczby są równie dobre jak „opinia”.
Rozczarowany

67

Zrobiłem również test dla przyszłych pracowników Google. Całkowita liczba zwróconych wyników wynosi 7264 na 10000

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

To zapytanie zajęło 0.1239kilka sekund

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

To zapytanie zajęło 0.0433kilka sekund

IN jest 3 razy szybszy niż OR


15
Czym był silnik MySQL i czy wyczyściłeś bufory MySQL i pamięci podręczne plików systemu operacyjnego między dwoma zapytaniami?
dabest1

2
Twój test jest wąskim przypadkiem użycia. Zapytanie zwraca 72% danych i jest mało prawdopodobne, aby skorzystało z indeksów.
Rozczarowany

Założę się, że większość tego czasu zajmowała zapytanie, analizowało je i planowało zapytanie. To z pewnością kwestia: jeśli będziesz miał 10k wyrażeń OR, będziesz miał dużo zbędnego tekstu, który po prostu go wyrazi OR: najlepiej użyć możliwie najbardziej zwartego wyrażenia.
biskup

17

Przyjęta odpowiedź nie wyjaśnia przyczyny.

Poniżej cytowano z High Performance MySQL, wydanie trzecie.

Na wielu serwerach baz danych IN () jest tylko synonimem wielu klauzul OR, ponieważ oba są logicznie równoważne. Nie jest tak w MySQL, który sortuje wartości na liście IN () i używa szybkiego wyszukiwania binarnego, aby sprawdzić, czy wartość znajduje się na liście. Jest to O (Log n) w wielkości listy, podczas gdy równoważna seria klauzul OR ma O (n) w wielkości listy (tj. Znacznie wolniej w przypadku dużych list)


Fantastyczne odniesienie do konkretnego powodu w bazie danych. Miły!
Joshua Pinter

Idealnie i na temat
gaurav9620

16

Myślę, że BETWEEN będzie szybszy, ponieważ należy go przekonwertować na:

Field >= 0 AND Field <= 5

Rozumiem, że IN i tak zostanie przekonwertowany na kilka instrukcji OR. Wartością IN jest łatwość użycia. (Oszczędzając konieczności wielokrotnego wpisywania nazwy każdej kolumny, a także ułatwia korzystanie z istniejącej logiki - nie musisz się martwić o pierwszeństwo AND / OR, ponieważ IN jest jedną instrukcją. Dzięki wielu instrukcjom OR masz aby upewnić się, że otaczasz je nawiasami, aby upewnić się, że są oceniane jako jeden warunek).

Jedyną prawdziwą odpowiedzią na twoje pytanie jest PROFILOWANIE SWOICH PYTAŃ . Wtedy będziesz wiedział, co działa najlepiej w twojej konkretnej sytuacji.


Statystycznie, Between ma szansę wyzwolić indeks zasięgu. IN () nie ma tego uprawnienia. Ale tak, plaża ma rację: POTRZEBUJESZ profilować swoją prośbę, aby wiedzieć, czy indeks jest używany i który. Naprawdę trudno jest przewidzieć, co wybierze optymalizator MySQL.
Savageman,

„Rozumiem, że IN i tak zostanie przekonwertowany na kilka instrukcji OR”. Gdzie to przeczytałeś? Spodziewałbym się, że umieści to w haszapie, aby wyszukiwać O (1).
Ztyx

Konwersja IN na OR jest sposobem, w jaki SQLServer sobie z tym radzi (a przynajmniej tak zrobił - mógł się teraz zmienić, nie używać go od lat). Nie udało mi się znaleźć żadnych dowodów na to, że MySQL to robi.
RichardAtHome

4
Ta odpowiedź jest poprawna, pomiędzy jest konwertowane na „1 <= film_id <= 5”. Pozostałe dwa rozwiązania nie są złożone w warunek pojedynczego zakresu. Mam wpis na blogu, który pokazuje to za pomocą ŚCIEŻKI
Morgan Tocker

13

To zależy od tego, co robisz; jak szeroki jest zakres, jaki jest typ danych (wiem, że twój przykład używa liczbowego typu danych, ale twoje pytanie może dotyczyć również wielu różnych typów danych).

Jest to instancja, w której chcesz napisać zapytanie w obie strony; uruchom go, a następnie użyj EXPLAIN, aby dowiedzieć się o różnicach w wykonaniu.

Jestem pewien, że istnieje konkretna odpowiedź na to pytanie, ale w ten sposób praktycznie wymyśliłbym odpowiedź na zadane pytanie.

Może to być pomocne: http://forge.mysql.com/wiki/Top10SQLPerformanceTips

Pozdrawiam,
Frank


2
To powinna być wybrana odpowiedź.
Jon z

3
Link jest nieaktualny - myślę, że może to być odpowiednik? wikis.oracle.com/pages/viewpage.action?pageId=27263381 (dzięki Oracle ;-P)
ilasno

1
Na równoważnej stronie jest napisane: „Unikaj używania IN (...) podczas wybierania w polach indeksowanych, zabije to wydajności zapytania SELECT”. - Masz pomysł, dlaczego tak jest?
jorisw

adres URL wygasł
Steve Jiang

7

Myślę, że jednym z wyjaśnień dla obserwacji osoby poszukującej słońca jest to, że MySQL faktycznie sortuje wartości w instrukcji IN, jeśli wszystkie są wartościami statycznymi i wykorzystują wyszukiwanie binarne, które jest bardziej wydajne niż zwykła alternatywa OR. Nie pamiętam, gdzie to przeczytałem, ale wynik poszukiwacza słońca wydaje się dowodem.


4

Właśnie wtedy, gdy myślałeś, że to bezpieczne ...

Jaka jest twoja wartość eq_range_index_dive_limit? W szczególności, czy w INklauzuli jest więcej lub mniej pozycji ?

Nie będzie to obejmowało testu porównawczego, ale trochę zagłębi się w wewnętrzne funkcjonowanie. Użyjmy narzędzia, aby zobaczyć, co się dzieje - Optymalizator Śledzenie.

Zapytanie: SELECT * FROM canada WHERE id ...

Przy OR3 wartościach część śledzenia wygląda następująco:

       "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"
          }
        ]

Zwróć uwagę, jak podaje się ICP ORs. To oznacza, że ORnie zmieniła się IN, a InnoDB będzie wykonanie pęczek= testów przez ICP. (Nie sądzę, że warto rozważyć MyISAM.)

(Jest to dziennik 5.6.22-71.0-Percona; idjest to indeks wtórny.)

Teraz dla IN () z kilkoma wartościami

eq_range_index_dive_limit= 10; jest 8 wartości.

        "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"
          }
        ]

Zauważ, że INnie wydaje się, aby zostało zamienione OR.

Uwaga dodatkowa: zauważ, że wartości stałe zostały posortowane . Może to być korzystne na dwa sposoby:

  • Skacząc mniej, może być lepsze buforowanie, mniej I / O, aby uzyskać wszystkie wartości.
  • Jeśli dwa podobne zapytania pochodzą z oddzielnych połączeń i dotyczą transakcji, istnieje większa szansa na opóźnienie zamiast impasu z powodu nakładających się list.

Na koniec IN () z dużą ilością wartości

      {
        "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"
          }
        ]

Uwaga dodatkowa: Potrzebowałem tego ze względu na dużą objętość śladu:

@@global.optimizer_trace_max_mem_size = 32222;

3

LUB będzie najwolniejszy. To, czy IN lub BETWEEN jest szybsze, będzie zależeć od twoich danych, ale spodziewam się, że BETWEEN będzie normalnie szybszy, ponieważ może po prostu wziąć zakres z indeksu (zakładając, że someField jest indeksowany).


3

Poniżej znajdują się szczegóły 6 zapytań wykorzystujących MySQL 5.6 @SQLFiddle

Podsumowując, 6 zapytań obejmuje niezależnie indeksowane kolumny i zastosowano 2 zapytania dla każdego typu danych. Wszystkie zapytania skutkowały użyciem indeksu niezależnie od użytej IN () lub OR.

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

Naprawdę chciałem po prostu obalić stwierdzenia, że ​​OR oznacza, że ​​nie można użyć indeksu. To nie jest prawda. Indeksów można używać w zapytaniach wykorzystujących OR jako 6 zapytań w poniższych przykładach.

Wydaje mi się również, że wielu zignorowało fakt, że IN () jest skrótem składni zestawu OR. W małej skali różnice w wydajności między użyciem IN () -v- OR są niezwykle (nieintesencjonalnie) marginalne.

Podczas gdy na większą skalę IN () jest z pewnością wygodniejszy, ale logicznie odpowiada zestawowi warunków OR. Zmiana okoliczności dla każdego zapytania, więc testowanie zapytania na tabelach jest zawsze najlepsze.

Podsumowanie 6 planów wyjaśniania, wszystkie „Korzystanie z warunku indeksu” (przewiń w prawo)

  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  

SQL Fiddle

Konfiguracja schematu 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
;

Zapytanie 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

Wyniki :

|          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 |

Zapytanie 2 :

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

Wyniki :

|          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 |

Zapytanie 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')

Wyniki :

|           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 |

Zapytanie 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')
        )

Wyniki :

|           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 |

Zapytanie 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'

Wyniki :

|           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 |

Zapytanie 6 :

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

Wyniki :

|          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 |

2

Założę się, że są takie same, możesz uruchomić test, wykonując następujące czynności:

przeskocz 500 razy „in (1,2,3,4)” i zobacz, jak długo to potrwa. 500 razy obejrzyj wersję „= 1 lub = 2 lub = 3 ...” i zobacz, jak długo ona działa.

możesz również wypróbować sposób łączenia, jeśli someField jest indeksem, a twoja tabela jest duża, może być szybszy ...

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

Wypróbowałem powyższą metodę łączenia na moim SQL Server i jest ona prawie taka sama jak w (1,2,3,4), i oba powodują przeszukiwanie indeksów klastrowych. Nie jestem pewien, jak MySQL sobie z nimi poradzi.



0

Z tego, co rozumiem na temat sposobu, w jaki kompilator optymalizuje tego rodzaju zapytania, użycie klauzuli IN jest bardziej wydajne niż wielu klauzul OR. Jeśli masz wartości, w których można zastosować klauzulę BETWEEN, jest to jeszcze bardziej wydajne.


0

Wiem, że dopóki masz indeks na polu, BETWEEN użyje go do szybkiego znalezienia jednego końca, a następnie przejdzie do drugiego. To jest najbardziej wydajne.

Każde WYJAŚNIENIE pokazuje, że „IN (...)” i „… OR…” są wymienne i równie (nie) skuteczne. Czego byś się spodziewał, ponieważ optymalizator nie ma możliwości sprawdzenia, czy zawierają one interwał. Jest to również równoważne z UNION ALL SELECT dla poszczególnych wartości.


0

Jak wyjaśniają inni, IN jest lepiej wybierany niż OR pod względem wydajności zapytania.

W poniższych przypadkach zapytania z warunkiem LUB mogą zająć więcej czasu.

  1. wykonać, jeśli optymalizator MySQL wybierze inny indeks, aby był wydajny (w przypadkach fałszywie dodatnich).
  2. Jeśli liczba rekordów jest większa (jak wyraźnie stwierdził Jakub)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.