## モジュール 2: EXPLAIN コマンドの使用

+ この演習では、TiDBのオプティマイザーの実行計画を複数の異なるSELECTクエリに対してEXPLAINコマンドを使用して調査します。

### 手順 1. 次のステートメントを実行してEXPLAIN出力を表示

  ```sql
  EXPLAIN SELECT * FROM parameterReference WHERE id=100;
  ```

  ```output
  tidb>EXPLAIN SELECT * FROM parameterReference WHERE id=100;
  +-------------+---------+------+--------------------------+---------------+
  | id          | estRows | task | access object            | operator info |
  +-------------+---------+------+--------------------------+---------------+
  | Point_Get_1 | 1.00    | root | table:parameterReference | handle:100    |
  +-------------+---------+------+--------------------------+---------------+
  1 row in set (0.00 sec)
  ```

+ このクエリのアクセス方法は何ですか？
+ 答え: `Point_Get`
+ TiDBはこのクエリを実行するために何行を読む必要がありますか？
+ 答え: 1
+ このアクセス戦略は良好なパフォーマンスをもたらしますか？理由を述べてください。
+ 答え: はい。`Point_Get` タイプは、`PRIMARY KEY` または `UNIQUE Index` のすべての部分を定数値と比較するときに適用されます。この例では、テーブルには最大で1つの一致する行が存在します。オプティマイザーはこの行のカラム値を定数として扱い、それらを一度だけ読み取ります。TiDBでは、`Point_Get` は実行計画を生成するためのコンパイラを必要とせず、データがTiKVにキーバリュー形式で格納されているため、`WHERE` 条件がプライマリキーまたはユニークインデックスである場合に `Point_Get` を使用して直接データを取得することができます。

### 手順 2. 次のステートメントを実行してEXPLAIN出力を表示

  ```sql
  EXPLAIN SELECT * FROM parameterReference WHERE id=100+5;
  ```

  ```output
  tidb>EXPLAIN SELECT * FROM parameterReference WHERE id=100+5;
  +-------------+---------+------+--------------------------+---------------+
  | id          | estRows | task | access object            | operator info |
  +-------------+---------+------+--------------------------+---------------+
  | Point_Get_5 | 1.00    | root | table:parameterReference | handle:105    |
  +-------------+---------+------+--------------------------+---------------+
  1 row in set (0.00 sec)
  ```

+ 手順1で見たクエリと比較して、このクエリの実行計画に違いはありますか？理由を述べてください。
+ 答え: いいえ。このオプティマイザーは式`100+5`をクエリを実行する前に評価します。

### 手順 3. 次のステートメントを実行してEXPLAIN出力を表示

  ```sql
  EXPLAIN SELECT * FROM parameterReference WHERE id-5=100;
  ```

  ```output
  tidb>EXPLAIN SELECT * FROM parameterReference WHERE id-5=100;
  +-------------------------+---------+-----------+--------------------------+---------------------------------------------------+
  | id                      | estRows | task      | access object            | operator info                                     |
  +-------------------------+---------+-----------+--------------------------+---------------------------------------------------+
  | TableReader_7           | 1644.00 | root      |                          | data:Selection_6                                  |
  | └─Selection_6           | 1644.00 | cop[tikv] |                          | eq(minus(universe.parameterreference.id, 5), 100) |
  |   └─TableFullScan_5     | 2055.00 | cop[tikv] | table:parameterReference | keep order:false                                  |
  +-------------------------+---------+-----------+--------------------------+---------------------------------------------------+
  3 rows in set (0.00 sec)
  ```

+ 手順1と2で見たクエリと比較して、このクエリの実行計画に違いはありますか？理由を述べてください。
+ 答え: はい。このオプティマイザーは式`ID-5=100`をクエリを実行する前に単一の値に還元できません。そのため、フルテーブルスキャン（タイプ: TableFullScan）を行う必要があります。行数は統計に基づく推定値であり、実際の`parameterReference`テーブルの行数と一致するとは限りません。

### 手順 4. JSON形式でクエリのオプティマイザーコストを表示

  ```sql
  CREATE INDEX idx_host_name_ref ON planetarySystem(hostname, pl_name, ref_id);
  EXPLAIN FORMAT = "tidb_json" SELECT pl_name, sy_snum, sy_pnum FROM planetarySystem WHERE pl_name='11 Com b' AND hostname='11 Com' AND ref_id=71\G
  DROP INDEX idx_host_name_ref on planetarySystem;
  ```

  ```output
  *************************** 1. row ***************************
  TiDB_JSON: [
     {
        "id": "Projection_4",
        "estRows": "1.00",
        "taskType": "root",
        "operatorInfo": "universe.planetarysystem.pl_name, universe.planetarysystem.sy_snum, universe.planetarysystem.sy_pnum",
        "subOperators": [
              {
                 "id": "IndexLookUp_10",
                 "estRows": "1.00",
                 "taskType": "root",
                 "subOperators": [
                    {
                          "id": "IndexRangeScan_8(Build)",
                          "estRows": "1.00",
                          "taskType": "cop[tikv]",
                          "accessObject": "table:planetarySystem, index:idx_host_name_ref(hostname, pl_name, ref_id)",
                          "operatorInfo": "range:[\"11 Com\" \"11 Com b\" 71,\"11 Com\" \"11 Com b\" 71], keep order:false"
                    },
                    {
                          "id": "TableRowIDScan_9(Probe)",
                          "estRows": "1.00",
                          "taskType": "cop[tikv]",
                          "accessObject": "table:planetarySystem",
                          "operatorInfo": "keep order:false"
                    }
                 ]
              }
        ]
     }
  ]

  1 row in set (0.01 sec)
  ```

+ このクエリに対してオプティマイザーが計算したコストはいくらですか？
+ 答え: 1.0 (JSON出力の"estRows"フィールドの値。このコストInfo JSONオブジェクトは、より詳細なコストの内訳を表示します。）
+ このクエリを解決するために、TiDBはどのカラムにアクセスする必要がありますか？
+ 答え: `universe.planetarysystem.pl_name`、`universe.planetarysystem.sy_snum`、`universe.planetarysystem.sy_pnum`カラム（JSON "operatorInfo" 配列フィールドの要素）
+ TiKVでどの操作が行われますか？
+ 答え: `IndexRangeScan` と `TableRowIDScan` がTiKVで行われます（JSON出力の`taskType`フィールドの値が`cop[tikv]`です）。
+ TiDBサーバでどの操作が行われますか？
+ 答え: `IndexLookUp` がTiDBサーバで行われます（JSON出力の "taskType" フィールドの値が "root" です）。
