MYSQL ORとINのパフォーマンス


180

以下の性能に違いはないかと思います

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

またはMySQLはコンパイラがコードを最適化するのと同じ方法でSQLを最適化しますか?

編集:コメントに記載されている理由により、ANDをからに変更しましたOR


イムもこのことを研究していs I could say that it can also be converted to UNIONますが、クエリを最適化するためにOR を置き換えるために推奨されるOR の行にINが変換されるといういくつかのステートメントに反対しています。
ジャニスGruzis

回答:


249

私はこれを確実に知る必要があったので、両方の方法をベンチマークしました。私は一貫してIN、を使用するよりもはるかに高速であることがわかりましたOR

「意見」を述べる人を信じないでください。科学はすべてテストと証拠に関するものです。

1000xの同等のクエリのループを実行しました(一貫性のために、私はを使用しましたsql_no_cache)。

IN:2.34969592094s

OR:5.83781504631s

更新:
(6年前のように、元のテストのソースコードはありませんが、このテストと同じ範囲の結果が返されます)

これをテストするためのいくつかのサンプルコードのリクエストで、これが最も簡単な使用例です。Eloquentを使用して構文を単純化すると、同等の生のSQLでも同じように実行されます。

$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
これらのテストで使用されたインデックスは何ですか?
eggyal 2013年

5
また、クエリを最適化したところ、INステートメントがに比べて約30%高速であることがわかりましたOR
Timo002 2014年

12
Do not believe people who give their "opinion"あなたは100%
正解

7
パフォーマンス上の理由(MariaDB(MySQLの新しい無料ブランチ)のドキュメントを引用):=> 列が整数の場合、整数渡す...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
「自分の「意見」を与える人々を信じてはいけない」の結果として:パフォーマンスの数値を、それらの数値を取得するために使用されるスクリプト、テーブル、インデックスを含めずに提供すると、それらを検証できなくなります。このように、数字は「意見」と同じくらい良いです。
2016

67

また、将来のGoogle社員のためのテストも行いました。返された結果の総数は10000のうち7264

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

このクエリは0.1239数秒かかりました

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

このクエリは0.0433数秒かかりました

IN の3倍速い OR


15
それはどのMySQLエンジンでしたか?また、2つのクエリの間にあるMySQLバッファーとOSファイルキャッシュをクリアしましたか?
dabest1

2
あなたのテストは狭いユースケースです。クエリはデータの72%を返し、インデックスの恩恵を受けることはほとんどありません。
2016

その時間のほとんどは、クエリの消費、解析、およびクエリの計画にかかっていたに違いありません。これは確かに考慮事項です。1万個のORステートメントを作成する場合は、それを単に表現するだけで多くの冗長テキストが作成されますOR。可能な限り最もコンパクトな式を使用するのが最善です。
ビショップ

17

受け入れられた答えは理由を説明していません。

以下は、High Performance MySQL、3rd Editionからの引用です。

多くのデータベースサーバーでは、IN()は複数のOR句の同義語です。MySQLではそうではありません。IN()リストの値をソートし、高速バイナリ検索を使用して、値がリストにあるかどうかを確認します。これは、リストのサイズがO(Log n)ですが、同等の一連のOR句は、リストのサイズがO(n)です(つまり、大きなリストの場合ははるかに遅くなります)。


特定のデータベース理由への素晴らしい参照。いいね!
Joshua Pinter

完璧で要点
gaurav9620

16

次のように変換する必要があるため、BETWEENはより高速になると思います。

Field >= 0 AND Field <= 5

INがとにかく一連のORステートメントに変換されることは私の理解です。INの値は、使いやすさです。(各列名を複数回入力する手間が省け、既存のロジックでの使用も簡単になります。INは1つのステートメントであるため、AND / ORの優先順位を気にする必要はありません。ORステートメントの束を使用すると、それらを括弧で囲んで、それらが1つの条件として評価されるようにします。)

あなたの質問に対する唯一の本当の答えは、あなたの質問のプロフィールです。次に、特定の状況で何が最適に機能するかがわかります。


統計的に、Betweenは範囲インデックスをトリガーする機会があります。IN()にはこの特権がありません。しかし、はい、ビーチは正しいです。インデックスが使用されているかどうか、およびどのインデックスが使用されているかを知るために、リクエストをプロファイルする必要があります。MySQLオプティマイザが選択するものを予測することは本当に難しいです。
サベージマン

「INがとにかく一連のORステートメントに変換されることは私の理解です。」どこでこれを読みましたか?私はそれをハッシュマップに入れてO(1)ルックアップを行うことを期待します。
Ztyx 14

INがORに変換されるのは、SQLServerがそれを処理する方法です(または、少なくともそれは実行されました-今は変更されている可能性があり、何年も使用していません)。MySQLがこれを行うという証拠を見つけることができませんでした。
RichardAtHome 2014

4
この答えは正解です。「1」は「1 <= film_id <= 5」に変換されます。他の2つのソリューションは、単一の範囲の条件に折りたたまれていません。私はここで、この使用OPTIMIZER TRACEを示してブログ記事を持っている:tocker.ca/2015/05/25/...
モルガンTocker

13

それはあなたが何をしているかに依存します。範囲の幅、データ型は何ですか(例では数値データ型を使用していますが、質問は多くの異なるデータ型にも当てはまります)。

これは、クエリを双方向で記述したい場合です。それを動作させてから、EXPLAINを使用して実行の違いを把握します。

これに対する具体的な答えがあると確信していますが、これが実際に言えば、私の与えられた質問に対する答えを理解する方法です。

これはいくつかの助けになるかもしれません:http : //forge.mysql.com/wiki/Top10SQLPerformanceTips

よろしく、
フランク


2
これが選択された答えです。
Jon z

3
リンクが古くなっています-これは同等であると思いますか?wikis.oracle.com/pages/viewpage.action?pageId=27263381(Oracleに感謝;-P)
ilasno

1
同等のページで、「インデックス付きフィールドを選択するときにIN(...)を使用しないでください。SELECTクエリのパフォーマンスが低下します。」-それはなぜですか?
jorisw 2013年

URLの有効期限が切れています
Steve Jiang

7

sunseekerの観察の説明の1つは、MySQLがINステートメントの値をすべて静的な値で、バイナリ検索を使用している場合に実際に並べ替えることです。これは、プレーンなORの代替よりも効率的です。どこで読んだか思い出せませんが、サンシーカーの結果は証拠のようです。


4

安全だと思ったとき...

あなたの価値はeq_range_index_dive_limit何ですか?特に、アイテムの数が多いか少ないかIN条項にですか?

これにはベンチマークは含まれませんが、内部の仕組みに少し目を通します。何が起こっているかを確認するためのツールを使用しましょう-オプティマイザトレース。

クエリ: SELECT * FROM canada WHERE id ...

OR3つの値の、トレースの一部は次のようになります。

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

ICPがどのように与えられているかに注意してくださいORs。これはがに変換されないこと意味し、InnoDBは一連のORIN= ICPを通じて一連テストをます。(MyISAMを検討する価値はないと思います。)

(これはPerconaの5.6.22-71.0ログでidあり、セカンダリインデックスです。)

次に、いくつかの値を持つIN()について

eq_range_index_dive_limit= 10; 8つの値があります。

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

INはに変換されていないようですOR

補足:定数値がソートされていることに注意してください。これは次の2つの点で有益です。

  • ジャンプを少なくすることで、キャッシュが改善され、すべての値に到達するためのI / Oが少なくなる可能性があります。
  • 2つの類似したクエリが別々の接続からのものであり、それらがトランザクション内にある場合、リストの重複によるデッドロックの代わりに遅延が発生する可能性が高くなります。

最後に、多くの値を持つIN()

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

補足:トレースがかさばるため、これが必要でした。

@@global.optimizer_trace_max_mem_size = 32222;

3

または最も遅くなります。INかBETWEENのどちらが速いかはデータに依存しますが、インデックスから範囲を単純に取ることができるため、通常はBETWEENの方が速いと思います(someFieldにインデックスが付けられていると仮定)。


3

以下は、MySQL 5.6 @SQLFiddleを使用した6つのクエリの詳細です

要約すると、6つのクエリは独立してインデックス付けされた列をカバーし、データ型ごとに2つのクエリが使用されました。使用されているIN()またはORに関係なく、すべてのクエリでインデックスが使用されました。

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

私は、ORがインデックスを使用できないことを意味するステートメントをデバンクしたいだけです。これは真実ではありません。次の例の6つのクエリが表示するように、ORを使用したクエリでインデックスを使用できます。

また、IN()がORのセットの構文ショートカットであるという事実を多くの人が無視しているようにも思えます。小規模では、IN()-v- ORを使用した場合のパフォーマンスの違いは、非常に(非本質的に)わずかです。

より大きなスケールではIN()の方が確かに便利ですが、論理的には一連のOR条件に相当します。クエリごとに状況が変化するため、テーブルでクエリをテストすることが常に最善です。

6つのExplainプランの要約、すべて「インデックス条件の使用」(右スクロール)

  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フィドル

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
;

クエリ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

結果

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

クエリ2

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

結果

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

クエリ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')

結果

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

クエリ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')
        )

結果

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

クエリ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'

結果

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

クエリ6

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

結果

|          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

私はそれらが同じであることを確信するでしょう、あなたは以下をすることによってテストを実行することができます:

「in(1,2,3,4)」を500回ループして、どれだけ時間がかかるかを確認します。「= 1 or = 2 or = 3 ...」バージョンを500回ループし、実行時間を確認します。

結合方法を試すこともできます。someFieldがインデックスであり、テーブルが大きい場合は、より高速になる可能性があります...

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

上記の結合方法をSQL Serverで試しましたが、(1,2,3,4)とほぼ同じで、どちらもクラスター化インデックスシークを発生させます。MySQLがそれらをどのように処理するかはわかりません。



0

コンパイラーがこれらのタイプの照会を最適化する方法について私が理解していることから、IN節の使用は、複数のOR節よりも効率的です。BETWEEN句を使用できる値がある場合でも、より効率的です。


0

Fieldにインデックスがある限り、BETWEENはそれを使用して、一方の端をすばやく見つけ、もう一方の端に移動することを知っています。これが最も効率的です。

私が見たすべてのEXPLAINは、「IN(...)」と「... OR ...」が互換性があり、等しく(非)効率的であることを示しています。これは、オプティマイザがインターバルを構成するかどうかを知る方法がないためです。また、個々の値に対するUNION ALL SELECTと同等です。


0

他の人が説明しているように、クエリのパフォーマンスに関しては、ORよりもINを選択する方が適切です。

OR条件を使用したクエリは、以下の場合に実行時間が長くなる可能性があります。

  1. MySQLオプティマイザが他のインデックスを効率的に選択した場合に実行する(誤検知の場合)。
  2. レコードの数が多い場合(Jacobによって明確に述べられているように)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.