無料PHPプログラム

MySQL 5.1 リファレンスマニュアル :: 6 最適化 :: 6.2 SELECTステートメントおよびその他のクエリの最適化 :: 6.2.1 EXPLAINを使用して、クエリを最適化する
« 6.2 SELECTステートメントおよびその他のクエリの最適化

6.2.2 クエリパフォーマンスの推定 »
Section Navigation      [Toggle]
  • 6.2 SELECTステートメントおよびその他のクエリの最適化
  • 6.2.1 EXPLAINを使用して、クエリを最適化する
  • 6.2.2 クエリパフォーマンスの推定
  • 6.2.3 SELECTクエリの速度
  • 6.2.4 WHERE 節最適化
  • 6.2.5 Range 最適化
  • 6.2.6 インデックス結合最適化
  • 6.2.7 IS NULL最適化
  • 6.2.8 DISTINCT最適化
  • 6.2.9 LEFT JOINとRIGHT JOIN最適化
  • 6.2.10 入れ子結合最適化
  • 6.2.11 外側Join 単純化
  • 6.2.12 ORDER BY最適化
  • 6.2.13 GROUP BY最適化
  • 6.2.14 LIMITの最適化
  • 6.2.15 テーブルスキャンを避ける方法
  • 6.2.16 INSERTステートメントの速度
  • 6.2.17 UPDATEステートメントの速度
  • 6.2.18 DELETEステートメントの速度
  • 6.2.19 その他の最適化のヒント

6.2.1. EXPLAINを使用して、クエリを最適化する

EXPLAIN tbl_name

または

EXPLAIN [EXTENDED | PARTITIONS] SELECT select_options

EXPLAINステートメントはDESCRIBEのシノニムとして使用するか、MySQLがどのようにSELECT ステートメントを実行するかの情報が得られます。

  • EXPLAIN tbl_nameはDESCRIBE tbl_name またはSHOW COLUMNS FROM tbl_nameのシノニムです。

  • キーワード EXPLAINを SELECTステートメントの前に置いた場合、MySQL によってテーブルの結合状況と順序に関する情報が提供され、テーブルの SELECTの処理方法が説明されます。

  • EXPLAIN PARTITIONSはMySQL 5.1.5から提供されています。区割りされたテーブルのクエリを調べるときに便利です。詳細については、 項15.3.4. 「パーティション情報の取得」 をご参照ください。

このセクションでは、クエリ実行情報を得るためのEXPLAINの2つめの使用方法を記述します。DESCRIBEとSHOW COLUMNSステートメントの詳細については、項12.3.1. 「DESCRIBE 構文」と項12.5.4.4. 「SHOW COLUMNS 構文」を参照してください。

EXPLAINを利用すると、より速くレコードを検索する SELECTを得るために、どの時テーブルにインデックスを追加しなければならないかを確認できます。 また、EXPLAINを使用して、オプティマイザがテーブルを最適な順序で結合しているかどうかも確認することができます。オプティマイザが特定の順番で結合を行うように強制するにはただSELECTでステートメントをはじめるのではく、SELECTステートメントに SELECT STRAIGHT_JOIN節を追加します。

最適化方法の選択に影響を及ぼすキーの、カーディナリティなどのテーブル統計を更新するために、ANALYZE TABLEを定期的に実行する必要があります。 項12.5.2.1. 「ANALYZE TABLE 構文」を参照してください。

EXPLAINはSELECTステートメントで使用される各テーブルに関する情報を返します。テーブルは、読み取られた順序に従って一覧表示されます。MySQL は、単一スイープ多結合メソッドを使用してすべての結合を解決します。これは、MySQL が最初のテーブルからレコードを読み取ってから、第 2 のテーブル、第 3 のテーブルといった順序で、一致するレコードの検索を行うことを意味します。すべてのテーブルの処理が終わると、選択したカラムと、さらに一致レコードがあるテーブルが検索されるまでのテーブル一覧のバックトラックが出力されます。次のレコードはこのテーブルから読み取られ、処理が次のテーブルから続行されます。

EXTENDEDキーワードが使用された時、EXPLAINはSHOW WARNINGSステートメントをEXPLAINステートメントの後で発行することで閲覧できる余分な情報を表示する。この情報は、SELECTステートメント内でオプティマイザがどのようにテーブル名とカラム名を認証するか、SELECTが再書き込みと最適化ルールの適用後どのように表示されるか、そして最適化プロセスの他の注意点なども表示します。EXPLAIN EXTENDEDはMySQL 5.1.12以降、filteredカラムも表示します。

注:EXTENDED とPARTITIONS キーワードを、同じEXPLAINステートメントで使用することはできません。

EXPLAINの各出力行は1つのテーブルの情報を提供し、各行は以下のカラムを含んでいます。

  • id

    SELECT識別子。クエリ内におけるこの SELECTの順序番号。

  • select_type

    SELECT節の種類、次のいずれかが示される。

    SIMPLE 単純なSELECT (UNIONやサブクエリを使用しない)。
    PRIMARY 最外部のSELECT。
    UNION 内の第2およびそれ以降のSELECTステートメント。
    DEPENDENT UNION UNION内の第2およびそれ以降のSELECTステートメント内のUNION、外側のサブクエリに依存する。
    UNION RESULT UNIONの結果。
    SUBQUERY サブクエリ内の第一SELECT。
    DEPENDENT SUBQUERY 第1SELECT、外側のサブクエリに依存する。
    DERIVED 派生テーブルSELECT (FROM節内のサブクエリ)
    UNCACHEABLE SUBQUERY 結果がキャッシュされず、外側のクエリの各行ごとに再評価されるサブクエリ。

    DEPENDENTは主に、相互に関係するサブクエリの使用を表します。項12.2.8.7. 「相関サブクエリ」を参照してください。

    「依存型サブクエリ」の評価はUNCACHEABLE SUBQUERY評価とは異なります。「DEPENDENT SUBQUERY」に関しては、外側コンテキストの変数の値が異なるたびに、一回のみサブクエリの再評価が行われます。UNCACHEABLE SUBQUERYに関しては、サブクエリは外側コンテキストの各行ごとに再評価されます。サブクエリのキャッシュアビリティは項4.13.1. 「クエリ キャッシュの動作」で記述される制限によります。例えば、ユーザ変数に参照することでサブクエリがキャッシュできなくなります。

  • テーブル

    結果を得るために参照するテーブル。

  • type

    結合型。各結合型を最適なものから順に紹介する。

    • システム

      1 レコードのみで構成されるテーブル(= システムテーブル)。これは、const結合型の特殊なケースである。

    • const

      テーブルに、一致するレコードが最大で 1 つあり、クエリの開始時に読み取られる。レコードが 1 つしかないため、このレコードのカラムの値はオプティマイザによって定数と見なされる。constテーブルは、1 回しか読み取られないため、非常に高速である。

      constはPRIMARY KEY/UNIQUEキーを定数と比較する場合に使用される。 .下記のクエリでは、tbl_nameはconstテーブルとして使用できる。

      SELECT * FROM tbl_name WHERE primary_key=1;
      
      SELECT * FROM tbl_name
        WHERE primary_key_part1=1 AND primary_key_part2=2;
      
    • eq_ref

      前のテーブルのレコードの組み合わせのそれぞれに対して、このテーブルから 1 レコードずつ読み取られる。これは、systemとconst型以外で最適な結合型である。 結合でインデックスのすべての部分が使用され、このインデックスが UNIQUEまたは PRIMARY KEYである場合に使用される。

      =演算子と比較されるインデックスの張られたカラムには、eq_refを使用できる。 較対象のアイテムは定数でも、このテーブル以前に読み取られたテーブルのカラムを使用する式でもかまわない。 下記の例では、ref_tableで eq_refが使用される。

      SELECT * FROM ref_table,other_table
        WHERE ref_table.key_column=other_table.column;
      
      SELECT * FROM ref_table,other_table
        WHERE ref_table.key_column_part1=other_table.column
        AND ref_table.key_column_part2=1;
      
    • ref

      前のテーブルのレコードの組み合わせのそれぞれに対して、インデックス値にマッチするすべてのレコードがこのテーブルから読み取られる。refは、インデックスの左端の先頭部分のみが結合で使用される場合、またはインデックスが UNIQUEや PRIMARY KEYではない場合(すなわち、この結合において、インデックス値から1つのレコードをSELECTできない場合)に使用される。この結合型は、使用されるインデックスと一致するレコードが数レコードしかない場合に適している。

      =あるいは<=>演算子と比較されるインデックスの張られたカラムには、ref を使用できる。 下記の例では、MySQLはref_tableで refが使用される。

      SELECT * FROM ref_table WHERE key_column=expr;
      
      SELECT * FROM ref_table,other_table
        WHERE ref_table.key_column=other_table.column;
      
      SELECT * FROM ref_table,other_table
        WHERE ref_table.key_column_part1=other_table.column
        AND ref_table.key_column_part2=1;
      
    • ref_or_null

      refと同様だが、NULLを使用したレコードの補足検索も追加で実行される。 この結合型の最適化は主としてサブクエリを解決する場合に使用される。 下記の例では、MySQLはref_tableで ref_or_nullが使用される。

      SELECT * FROM ref_table
        WHERE key_column=expr OR key_column IS NULL;
      

      項6.2.7. 「IS NULL最適化」参照。

    • index_merge

      この結合型はインデックス併合最適化が使用されたことを示しています。この場合、出力行のkeyカラムは使用されたインデックスのリストが含まれ、key_lenには使用されたインデックスの最長キー部分が含まれます。詳細は 項6.2.6. 「インデックス結合最適化」 をご覧ください。

    • unique_subquery

      この型は、下記のフォームでINサブクエリの代わりに、refを使用します。

      value IN (SELECT primary_key FROM single_table WHERE some_expr)
      

      unique_subqueryは、効率化のためサブクエリの代わりをつとめるインデックスルックアップ関数です。

    • index_subquery

      この結合型はunique_subqueryに似ています。INサブクエリの代わりに使用されますが、下記のサブクエリのフォームでユニークではないインデックスで使用できます。

      value IN (SELECT key_column FROM single_table WHERE some_expr)
      
    • range

      インデックスを使用して、一定の範囲にあるレコードのみが取り出される。keyカラムに使用されるインデックスが示される。key_len_には使用される最長のインデックス部分が記載される。 この型ではrefカラムがNULLになる。

      range は、インデックスを張っているカラムが =、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN、およびIN を使用して定数と比較される場合に使用される。

      SELECT * FROM tbl_name
        WHERE key_column = 10;
      
      SELECT * FROM tbl_name
        WHERE key_column BETWEEN 10 and 20;
      
      SELECT * FROM tbl_name
        WHERE key_column IN (10,20,30);
      
      SELECT * FROM tbl_name
        WHERE key_part1= 10 AND key_part2 IN (10,20,30);
      
    • index

      これは、インデックスツリーのみがスキャンされる点を除いて ALLと同じである。一般にインデックスファイルはデータファイルより小さいため、通常は ALLより高速である。

      MySQL は、クエリで 1 インデックスの構成部分であるカラムのみが使用される場合にのみ使用できます。

    • ALL

      前のテーブルのレコードの組み合わせのそれぞれに対して、フルテーブルスキャンが実行される。一般に、テーブルが constの指定がない第 1 テーブルの場合には適さず、その他の場合はすべて非常に不適である。 通常は、さらにインデックスを追加することで ALLを回避し、定数値または以前のテーブルのカラム値を基準にレコードを取り出すようにすることができる。

  • possible_keys

    possible_keysカラムは、このテーブル内のレコードの検索に MySQL で使用可能なインデックスを示す。このカラムはEXPLAINからの出力により表示されたテーブルの順序にはまったく依存しないことに注意する。すなわち、possible_keysのキーの一部は、生成されたテーブルの順序では事実上使用できないことになる。

    このカラムが NULLの場合は、対応するインデックスがない。tこの場合は、WHERE節でインデックス作成に適するカラムを 1 つ以上参照しているかどうかを調べることでクエリのパフォーマンスを改善できる。 参照している場合は適切なインデックスを作成し、再度 EXPLAINを使用してクエリをチェックする。 項12.1.2. 「ALTER TABLE 構文」を参照してください。

    テーブルにあるインデックスを調べるには SHOW INDEX FROM tbl_nameを使用する。

  • key

    keyカラムは、MySQL が実際に使用を決定したキー(インデックス)を示す。 MySQL が行をルックアップするためpossible_keysインデックスを使用した場合、キー値としてそのインデックスがリストされる。

    keyはpossible_keys 値に存在しないインデックスを指名する可能性もあります。これはpossible_keysインデックスのうちどれも行をルックアップするのに適していない場合におこりますが、クエリに選択された全てのカラムは他のインデックスのカラムになります。つまり、指名されたインデックスが選択されたカラムをカバーします。どの行を取得するか判別するのに使用されていなくとも、データ行スキャンよりもインデックススキャンの方が効率的です。

    InnoDBでは、クエリがプライマリキーを選択していてもセカンダリインデックスが選択されたカラムをカバーするかもしれません。これはクエリがプライマリキーを選択した場合もありえるのは、InnoDBが各セカンダリインデックスと共にプライマリキー値も保存するからです。MySQLがクエリを効率的に実行するインデックスを見つけられなかった場合、このkeyは NULLになる。

    MySQLで possible_keysカラムに記載されたキーが使用されるように強制するには、クエリでFORCE INDEX、USE INDEX、またはIGNORE INDEXを使用する。 項12.2.7. 「SELECT 構文」を参照してください。

    MyISAMテーブルには、ANALYZE TABLEを実行することでオプティマイザでより適したインデックスを選択する際役立つ。MyISAMテーブルに関しても、myisamchk --analyzeは同じことをします。項12.5.2.1. 「ANALYZE TABLE 構文」、項4.9.4. 「テーブル保守とクラッシュ リカバリ」 を参照して下さい。

  • key_len

    key_lenカラムは、MySQL が実際に使用を決定したキーの長さを示す。 keyが NULLの場合、この長さは NULLになる。 key_lenの値によって、複合キーで MySQL が実際に使用するパート数が示されることに注意する。

  • ref

    refカラムは、テーブルからレコードを選択する際に keyとともに使用されるカラムまたは定数を示す。

  • rows

    rowsカラムは、クエリの実行に際して調べる必要があると MySQL によって判定されたレコードの数を示す。

  • filtered

    filteredカラムはテーブルの状態によってフィルターされるテーブル行のパーセンテージ(予想)を表示します。つまり、rowsは検査された行の予想数を表示し、rows × filtered / 100は前のテーブルと結合する行の数を表示します。EXPLAIN EXTENDEDを使用すると、このカラムが表示されます。(MySQL 5.1.12の新しい機能です。)

  • Extra:

    このカラムには、MySQL でどのようにクエリが解決されるかに関する追加情報が記載される。下記のリストはこのカラムで表示される可能性のある値を説明する。クエリの速度をできる限り上げたい場合は、Using filesort とUsing temporaryのExtra値に注目してください。

    • Distinct

      マッチした最初のレコードが検索されると、MySQL は現在のレコードの組み合わせによるその後のレコード検索を続行しないことを示す。

    • const tablesを読んだ後 Impossible WHERE発見

      MySQL は全てのconst (あと、system) テーブルを読んだ後、WHERE節が常に偽となります。

    • No tables

      クエリにはFROM節がないか、FROM DUAL節があります。

    • Not exists

      MySQL でクエリに対する LEFT JOIN最適化が実行でき、LEFT JOINに一致するレコードが 1 つ検索されると、前のレコードの組み合わせによるその後のテーブルのレコードについては調べないことを示す。 このように最適化できるクエリの例を以下に示します。

      SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
        WHERE t2.id IS NULL;
      

      t2.idが NOT NULLで定義されているとする。この場合、MySQL で t1がスキャンされ、t1.idで t2内のレコードのルックアップが行われる。MySQL によって t2内のマッチするレコードが検索されると、t2.idはNULLではないと認識され、t2内の同じ idを持つ残りのレコードのスキャンは行われない。言い換えると、t2にあるマッチするレコードの数に関わらず、MySQL で実行が必要なことは t1のレコードのそれぞれに対して、t2のルックアップを 1 回実行することだけである。

    • range checked for each record (index map: N)

      MySQL で使用に適した実際のインデックスを検索できなかったことを示す。代替として、先行テーブルのレコードの組み合わせのそれぞれに対して、使用するインデックス(存在する場合)range またはindex_mergeのチェックが実行され、このインデックスがテーブルからのレコードの取り出しに使用される。非常に高速ではないが、インデックスなしの結合と比較すると高速である。 適用基準は項6.2.5. 「Range 最適化」と項6.2.6. 「インデックス結合最適化」で説明されています。ただし、これは前テーブルの全てのカラム値が知られており、定数であるという前提においてです。

    • Select tables optimized away

      クエリはMyISAM用に、インデックスで解決された集約ファンクション(MIN()、MAX())そしてCOUNT(*)があり、GROUP BY節は含みませんでした。オプティマイザは1つの行のみが返されるべきと判断しました。

    • Using filesort

      レコードをソートして取り出す方法を決定するには、MySQL はパスを余分に実行しなくてはならないことを示す。 join type に従ってすべてのレコードをスキャンし、WHERE 条件に一致する全てのレコードに、ソートキー + 行ポインタを格納して、ソートは実行される。その後キーがソートされる。 最後に、ソートされた順にレコードが取り出される。項6.2.12. 「ORDER BY最適化」を参照してください。

    • Using index

      インデックスツリーの情報のみを使用してカラム情報がテーブルから取り出され、実際の行を読み取るその後の検索を実行する必要がないことを示す。MySQL は、クエリで 1 インデックスの構成部分であるカラムのみが使用される場合にのみ使用できます。

    • Using temporary

      クエリの解決に MySQL で結果を保持するテンポラリテーブルの作成が必要であることを示す。これは一般に、GROUP BYを実行したカラムセットと異なるカラムセットに対して ORDER BYを実行した場合に発生する。

    • Using where

      次のテーブルとの一致が調べられるレコードまたはクライアントに送信されるレコードの限定に WHERE節が使用されることを示す。 この情報がなく、Extraの値がUsing whereではなく、テーブルの型が ALLまたは indexである場合はクエリが正常に実行されないことがある(テーブルのすべてのレコードの取得や検査を意図していない場合)。

    • Using sort_union(...)、Using union(...)、Using intersect(...)

      これらはindex_merge結合型でインデックススキャンがどのように併合されるかを示しています。詳細は 項6.2.6. 「インデックス結合最適化」 を参照。

    • Using index for group-by

      Using indexを使用してテーブルをアクセスする方法に似て、Using index for group-byはMySQLが余分なディスクアクセスを実際のテーブルに行うことなく、GROUP BYまたはDISTINCTクエリのカラムを全て取得することができるインデックスを見つけたことを意味します。加えて、インデックスは各グループにとって最も効率的に使われるので、数種類のインデックスしか読まれません。詳細については、項6.2.13. 「GROUP BY最適化」をご参照ください。

    • Using where with pushed condition

      このアイテムはNDB Clusterテーブルにのみ適用されます。それはMySQL クラスタがcondition pushdownを使用して行う、インデックスのないカラムと定数を直接比較(=)の効率化を図ることを意味します。その場合、状態はクラスターのデータノードに「押し戻され」ており、全てのパーティションで同時に評価されます。これはマッチしない行をネットワーク上で送る必要を無くし、コンディションプッシュダウンが使える状態にあり、使用しないケースでそのようなクエリの速度を5乗から10乗に増やす。

      以下のように定義されたクラスタテーブルがあるとします。

      CREATE TABLE t1 (
          a INT, 
          b INT, 
          KEY(a)
      ) ENGINE=NDBCLUSTER;
      

      この場合、コンディションプッシュダウンは下記のようなクエリで使用できます。

      SELECT a,b FROM t1 WHERE b = 10;
      

      これはEXPLAIN SELECTの出力で見られます。例えば

      mysql> EXPLAIN SELECT a,b FROM t1 WHERE b = 10\G
      *************************** 1. row ***************************
                 id: 1
        select_type: SIMPLE
              table: t1
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 10
              Extra: Using where with pushed condition
      

      コンディションプッシュダウンは下記の2つのクエリと一緒には使用できません。

      SELECT a,b FROM t1 WHERE a = 10;
      SELECT a,b FROM t1 WHERE b + 1 = 10;
      

      この二つのクエリのうち最初のものに関しては、インデックスがaカラムに存在するため、コンディションプッシュダウンは適用できません。2番目のクエリの場合、インデックスのないカラムbに関する比較は直接的でないため、コンディションプッシュダウンが適用できません。(ただし、b + 1 = 10をWHERE 節内でb = 9に減らす場合は適用されます。)

      ただし、>または<演算子を使用している定数とインデックスカラムが比較された場合、コンディションプッシュダウンが使用される場合もあります。

      mysql> EXPLAIN SELECT a,b FROM t1 WHERE a<2\G
      *************************** 1. row ***************************
                 id: 1
        select_type: SIMPLE
              table: t1
               type: range
      possible_keys: a
                key: a
            key_len: 5
                ref: NULL
               rows: 2
              Extra: Using where with pushed condition
      

      コンディションプッシュダウンに関して、以下のことに留意してください。

      • コンディションプッシュダウンはMySQLクラスタにのみ関連しており、他の保存エンジンを使用するテーブルに対してクエリを実行するときは起こりえません。

      • コンディションプッシュダウン機能はデフォルトでは使用されません。起動するには、mysqld を--engine-condition-pushdownオプションで使用するか、以下のステートメントを実行してください。

        SET engine_condition_pushdown=On;
        

        注:コンディションプッシュダウンはBLOB やTEXTタイプのどのカラムに対してもサポートされていません。

EXPLAIN出力の rowsカラムのすべての値を掛け算することで、結合がどの程度適しているかを示す指針を取得できます。Thisこれは、クエリの実行時に MySQL で調べる必要があるレコード数の概要を示します。この数値は、max_join_size変数でクエリを制限する際にも使用される他、どのマルチテーブルSELECTステートメントを実行するか、あるいはアボートするかを判別します。項6.5.2. 「サーバパラメータのチューニング」を参照してください。

下記の例は、EXPLAINによって得られた情報を使用して、マルチテーブルjoinを累進的に最適化する方法を示しています。

ここでは、EXPLAINを使用して、SELECTステートメントを調べるとします。

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
               tt.ProjectReference, tt.EstimatedShipDate,
               tt.ActualShipDate, tt.ClientID,
               tt.ServiceCodes, tt.RepetitiveID,
               tt.CurrentProcess, tt.CurrentDPPerson,
               tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
               et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
          AND tt.ActualPC = et.EMPLOYID
          AND tt.AssignedPC = et_1.EMPLOYID
          AND tt.ClientID = do.CUSTNMBR;

この例では以下のように想定しています。

  • 比較対象のカラムは以下のように宣言されます。

    テーブル カラム データ型
    tt ActualPC CHAR(10)
    tt AssignedPC CHAR(10)
    tt ClientID CHAR(10)
    et EMPLOYID CHAR(15)
    do CUSTNMBR CHAR(15)
  • テーブルには以下のインデックスがあります。

    テーブル インデックス
    tt ActualPC
    tt AssignedPC
    tt ClientID
    et EMPLOYID (プライマリキー)
    do CUSTNMBR (プライマリキー)
  • tt.ActualPC値の分布が均一ではない。

当初、最適化の実行前は、EXPLAINステートメントで次の情報が生成されました。

table type possible_keys key  key_len ref  rows  Extra
et    ALL  PRIMARY       NULL NULL    NULL 74
do    ALL  PRIMARY       NULL NULL    NULL 2135
et_1  ALL  PRIMARY       NULL NULL    NULL 74
tt    ALL  AssignedPC,   NULL NULL    NULL 3872
           ClientID,
           ActualPC
      range checked for each record (key map: 35)

各テーブルで typeが ALL であるため、この出力は MySQL がすべてのテーブルのデカルト積を生成すると示しています。各テーブルのレコードの数の積の分量を調べる必要があるため、これは非常に時間がかかります。この例の場合は、レコードの数が 74 ×2135 ×74 ×3872 = 45,268,558,720 になります。テーブルがこれより大きい場合は、さらに時間がかかると考えられます。

ここでの問題の 1 つは、宣言の方法が異なると MySQL でカラムのインデックスを効率的に使用できないことにあります。この例では、VARCHARと CHARが異なる長さで宣言されていなければ同じになります。tt.ActualPCが CHAR(10)として、et.EMPLOYIDが CHAR(15)として宣言されているため、長さの不一致が発生します。

カラムの長さの不一致を修正するため、ALTER TABLEを使用して ActualPCを 10 文字から 15 文字にします。

mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

これで tt.ActualPCと et.EMPLOYIDはいずれも VARCHAR(15)になりました。 ここでまた EXPLAINを実行してみると、以下の結果が得られました。

table type   possible_keys key     key_len ref         rows    Extra
tt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using
             ClientID,                                         where
             ActualPC
do    ALL    PRIMARY       NULL    NULL    NULL        2135
      range checked for each record (key map: 1)
et_1  ALL    PRIMARY       NULL    NULL    NULL        74
      range checked for each record (key map: 1)
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC 1

これも完全ではありませんが、かなり改善されています(rows値の積が 74 の係数分だけ減少)。このバージョンの場合実行に数秒かかります。

第 2 の変更を加えると、tt.AssignedPC = et_1.EMPLOYIDと tt.ClientID = do.CUSTNMBRの比較でのカラム長の不一致を解消できます。

mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
    ->                MODIFY ClientID   VARCHAR(15);

ここでは、EXPLAINから以下の出力が生成されます。

table type   possible_keys key      key_len ref           rows Extra
et    ALL    PRIMARY       NULL     NULL    NULL          74
tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using
             ClientID,                                         where
             ActualPC
et_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY  15      tt.ClientID   1

これでほとんど改善されています。 残りの問題は、MySQL ではデフォルトで tt.ActualPCカラムの値の分布が均一であると想定されますが、ttテーブルはこれにあてはまらないことです。これは容易に MySQL に示すことができます。

mysql> ANALYZE TABLE tt;

この追加インデックス情報で、結合が完全になり、EXPLAINで以下の結果が生成されます。

table type   possible_keys key     key_len ref           rows Extra
tt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using
             ClientID,                                        where
             ActualPC
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1
et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY 15      tt.ClientID   1

EXPLAINの出力の rowsカラムは、MySQL 結合オプティマイザの学習による推測であることに注意してください。クエリを最適化するには、この数値が実際に近いものであるかどうかを確認するためにrowsのプロダクトとクエリが実際に返す行の数をを比較する必要があります。実際とかけ離れている場合は、SELECTステートメントで STRAIGHT_JOINを使用し、FROM節でテーブルの順序を変えて一覧表示してみるとパフォーマンスを改善できます。

MySQL Enterprise.? MySQL ネットワーク監視とアドバイスサービス加入者は定期的にプロから最適化のアドバイスを提供されます。追加情報については http://www-jp.mysql.com/products/enterprise/advisors.htmlを参照してください。

Copyright c 1997, 2010, Oracle and/or its affiliates. All rights reserved. Legal Notices
Top / Previous / Next / Up / Table of Contents
© 2010, Oracle Corporation and/or its affiliates

無料CGI PHPスクリプト | 新着情報スクリプト | 営業日カレンダー | PHPマニュアル | MySQLマニュアル | PEARマニュアル

Copyright (c) 2010 jmcodex.com All rights reserved.