2016-05-20 8 views
3

私は2つのテーブルidentitieseventsを持っています。なぜこのMySQL INはWHERE ORよりも時間がかかりますか?

identitiesは、2つの列、identity1およびidentity2を持ち、どちらもハッシュインデックスを持っています。

eventsには〜50の列があり、列_pにはハッシュインデックスがあります。

CREATE TABLE `identities` (
    `identity1` varchar(255) NOT NULL DEFAULT '', 
    `identity2` varchar(255) DEFAULT NULL, 
    UNIQUE KEY `uniques` (`identity1`,`identity2`), 
    KEY `index2` (`identity2`) USING HASH, 
    KEY `index1` (`identity1`) USING HASH 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

-

CREATE TABLE `events` (
    `rowid` int(11) NOT NULL AUTO_INCREMENT, 
    `_p` varchar(255) NOT NULL, 
    `_t` int(10) NOT NULL, 
    `_n` varchar(255) DEFAULT '', 
    `returning` varchar(255) DEFAULT NULL, 
    `referrer` varchar(255) DEFAULT NULL, 
    `url` varchar(255) DEFAULT NULL, 

    [...] 

    `fcc_already_sells_online` varchar(255) DEFAULT NULL, 
    UNIQUE KEY `_p` (`_p`,`_t`,`_n`), 
    KEY `rowid` (`rowid`), 
    KEY `IDX_P` (`_p`) USING HASH 
) ENGINE=InnoDB AUTO_INCREMENT=5231165 DEFAULT CHARSET=utf8; 

それでは、なぜこのクエリん:

SELECT SQL_NO_CACHE * FROM events WHERE _p = '[email protected]' OR _p = '[email protected]' OR _p = '[email protected]' OR _p = '[email protected]' ORDER BY _t 

のみ20msのとき、彼らを取ります。この1つは一方で

SELECT SQL_NO_CACHE * FROM events WHERE _p IN (SELECT identity2 FROM identities WHERE identity1 = '[email protected]') ORDER BY _t 

は、〜40秒かかります基本的に同じですか?


編集:

この内部クエリが取る3,3ms:

SELECT SQL_NO_CACHE identity2 FROM identities WHERE identity1 = '[email protected]'

+0

「WHEREフィールドIN(値リスト)」は、WHEREフィールド=値1 ORフィールド=値2 OR ...として扱うことができますが、WHERE IN(サブクエリ)はできません。 MySQLのためのもの。あなたはもう少し[ここ]を読むことができます(http://stackoverflow.com/questions/19654878/inner-join-select-ab-on-a-and-b-vs-where-ab-in-selecta-b- in-mysql/19655121#19655121) –

+0

ニース、@AlmaDo、これを回答として送ってください、私はそれを受け入れます。 – dmmd

+0

また、答えについてもう少し詳しく説明できれば、それは素晴らしいことでしょう。 – dmmd

答えて

2

原因:

MySQLの扱い条件IN <static values list>と異なるものとしてIN <sub-query>documentationには、そのインデックスが存在してもインデックスを使用できないクエリが2番目のクエリであることがよく記載されています(= ANY())。 MySQLはそれを行うのに十分なほど独創的ではありません。反対に、最初のものは、単純な範囲スキャンとして扱われます。インデックスがあると、MySQLは簡単にインデックスを使用できます。解決する

可能な方法:

私はそれを見ると、そこの回避策があり、あなたはすでにでもそれらのいずれかを言及しました。したがって、次のようになります。

  • JOINを使用します。参加するフィールドがある場合、これは問題を解決する最も可能性の高い方法です。実際には、バージョン5.6からMySQLはすでにtries to enforce this optimizationが可能ですが、複雑なケースや依存サブクエリがない場合(基本的にMySQLがその参照を「追跡」できない場合)は動作しません。あなたのケースを見ると、これはオプションではありません。これは実際にはではなく、がサブクエリのために起こっています。
  • アプリケーション内のサブリソースをクエリし、静的リストを形成します。はい、一般的なプラクティスは、接続/ネットワーク/クエリ計画オーバーヘッドのために複数のクエリを避けることですが、これは実際に動作する場合です。あなたのケースでは、たとえ以前のすべての情報に200msのオーバーヘッドがあっても、サブリソースを独立してクエリし、後でアプリケーション内の静的リストを次のクエリに置き換える価値があります。
+0

完璧に、ありがとう! – dmmd

-1

this is already asked

それが定義する唯一の構造であるため、IN演算子を管理する方が簡単です=演算子が同じ値の複数の条件のOR演算子。 OR演算子を使用する場合、オプティマイザは、同じ値に対して常に=演算子を使用しているとはみなさない可能性があります。

+0

これは同じ質問ではありません...私は内側のクエリを使用していますが、そうではありません。 – dmmd

-1

イベントテーブルの各行に対してこの内部クエリが呼び出されているためです。

第2の場合、インデントテーブルは使用されません。

代わりに参加する必要があります。

+0

ありがとうございます。私はどこで実行するために行のセットを返すように私は結合を使用することはできません。 – dmmd

+0

はい、可能です: SELECT SQL_NO_CACHE *イベントからINNER JOINアイデンティティi ON e._p = i.identity2 WHERE identity1 = '[email protected]' ORDER BY _t – mjpolak

+0

この方法では、 IN?の静的な値を使用しますか? JOINがこれに適した回避策であるかどうかは不明ですが、あなたの答えを精緻化してください。 (PSはあなたに落とした私ではなかった) – dmmd

関連する問題