## Module 4: Indexing Strategies with JOIN

+ In this module, we optimize the following two queries:

   ```sql
   SELECT A.pl_name, A.hostname, A.discoverymethod, A.disc_year,B.name as DiscoveryFacility, C.ref_name as ParameterReference 
   FROM planetarySystem A, discoveryFacility B, parameterReference C 
   WHERE A.df_id = B.id AND A.ref_id = C.id AND C.ref_name LIKE 'Rosenthal%' AND A.hostname LIKE '14 Her' AND A.disc_year > 2020;
   ```

   ```sql
   SELECT * FROM planetarySystem WHERE ref_id IN ( SELECT ID FROM parameterReference WHERE ref_name LIKE 'Rosenthal%');
   ```

### Step 1. Check the execution plan for the following query

   ```sql
   EXPLAIN SELECT A.pl_name, A.hostname, A.discoverymethod, A.disc_year,B.name as DiscoveryFacility, C.ref_name as ParameterReference 
   FROM planetarySystem A, discoveryFacility B, parameterReference C 
   WHERE A.df_id = B.id AND A.ref_id = C.id AND C.ref_name LIKE 'Rosenthal%' AND A.hostname LIKE '14 Her' AND A.disc_year > 2020;
   ```

   ```output
   +------------------------------------+----------+-----------+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | id                                 | estRows  | task      | access object | operator info                                                                                                                                                                                                            |
   +------------------------------------+----------+-----------+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | Projection_13                      | 1.98     | root      |               | universe.planetarysystem.pl_name, universe.planetarysystem.hostname, universe.planetarysystem.discoverymethod, universe.planetarysystem.disc_year, universe.discoveryfacility.name, universe.parameterreference.ref_name |
   | └─IndexJoin_18                     | 1.98     | root      |               | inner join, inner:TableReader_15, outer key:universe.planetarysystem.df_id, inner key:universe.discoveryfacility.id, equal cond:eq(universe.planetarysystem.df_id, universe.discoveryfacility.id)                        |
   |   ├─IndexJoin_31(Build)            | 1.98     | root      |               | inner join, inner:TableReader_27, outer key:universe.planetarysystem.ref_id, inner key:universe.parameterreference.id, equal cond:eq(universe.planetarysystem.ref_id, universe.parameterreference.id)                    |
   |   │ ├─TableReader_40(Build)        | 1.98     | root      |               | data:Selection_39                                                                                                                                                                                                        |
   |   │ │ └─Selection_39               | 1.98     | cop[tikv] |               | gt(universe.planetarysystem.disc_year, 2020), like(universe.planetarysystem.hostname, "14 Her", 92)                                                                                                                      |
   |   │ │   └─TableFullScan_38         | 34713.00 | cop[tikv] | table:A       | keep order:false                                                                                                                                                                                                         |
   |   │ └─TableReader_27(Probe)        | 0.00     | root      |               | data:Selection_26                                                                                                                                                                                                        |
   |   │   └─Selection_26               | 0.00     | cop[tikv] |               | like(universe.parameterreference.ref_name, "Rosenthal%", 92)                                                                                                                                                             |
   |   │     └─TableRangeScan_25        | 1.98     | cop[tikv] | table:C       | range: decided by [universe.planetarysystem.ref_id], keep order:false                                                                                                                                                    |
   |   └─TableReader_15(Probe)          | 1.98     | root      |               | data:TableRangeScan_14                                                                                                                                                                                                   |
   |     └─TableRangeScan_14            | 1.98     | cop[tikv] | table:B       | range: decided by [universe.planetarysystem.df_id], keep order:false                                                                                                                                                     |
   +------------------------------------+----------+-----------+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   11 rows in set (0.01 sec)
   ```

+ Which two tables are joined first? What type of table join is used?
  + Answer: Table `planetarySystem` (A) and `parameterReference` (C) are joined first, the join type is `IndexJoin`.
+ Which table only participates in the second table join? What type of table join is used?
  + Answer: Table `discoveryFacility` (B), the join type is `IndexJoin`.
+ What is the access method for each table query?
  + Answer: `TableFullScan`, `Selection` and `TableReader` for table `planetarySystem` (A); `TableRangeScan`,  `Selection` and `TableReader` for table `parameterReference` (C); `TableRangeScan` and `TableReader` for table `discoveryFacility` (B).
+ Does this access strategy result in good performance? Why?
  + Answer: No. This is because in the `WHERE` clause, you see filter conditions like `C.ref_name LIKE 'Rosenthal%' AND A.hostname LIKE '14 Her' AND A.disc_year > 2020`. We can create indexes to accelerate these filter operations in TiKV nodes and avoid `TableFullScan` or other filtering actions such as `SELECT`, in order to reduce IO pressure.

### Step 2. Add indexes and check the execution plan for the following query

   ```sql
   CREATE INDEX idx_hostname_disc_year ON planetarySystem(hostname, disc_year);
   CREATE INDEX idx_ref_name ON parameterReference(ref_name);
   ANALYZE TABLE planetarySystem;
   ANALYZE TABLE parameterReference;
   ```

   ```output
   Query OK, 0 rows affected, 1 warning (0.63 sec)
   ```

   ```sql
   EXPLAIN SELECT A.pl_name, A.hostname, A.discoverymethod, A.disc_year,B.name as DiscoveryFacility, C.ref_name as ParameterReference 
   FROM planetarySystem A, discoveryFacility B, parameterReference C 
   WHERE A.df_id = B.id AND A.ref_id = C.id AND C.ref_name LIKE 'Rosenthal%' AND A.hostname LIKE '14 Her' AND A.disc_year > 2020;
   ```

   ```output
   +------------------------------------------+---------+-----------+------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | id                                       | estRows | task      | access object                                              | operator info                                                                                                                                                                                                            |
   +------------------------------------------+---------+-----------+------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | Projection_13                            | 1.98    | root      |                                                            | universe.planetarysystem.pl_name, universe.planetarysystem.hostname, universe.planetarysystem.discoverymethod, universe.planetarysystem.disc_year, universe.discoveryfacility.name, universe.parameterreference.ref_name |
   | └─IndexJoin_18                           | 1.98    | root      |                                                            | inner join, inner:TableReader_15, outer key:universe.planetarysystem.df_id, inner key:universe.discoveryfacility.id, equal cond:eq(universe.planetarysystem.df_id, universe.discoveryfacility.id)                        |
   |   ├─HashJoin_37(Build)                   | 1.98    | root      |                                                            | inner join, equal:[eq(universe.planetarysystem.ref_id, universe.parameterreference.id)]                                                                                                                                  |
   |   │ ├─IndexLookUp_43(Build)              | 1.98    | root      |                                                            |                                                                                                                                                                                                                          |
   |   │ │ ├─IndexRangeScan_41(Build)         | 1.98    | cop[tikv] | table:A, index:idx_hostname_disc_year(hostname, disc_year) | range:("14 Her" 2020,"14 Her" +inf], keep order:false                                                                                                                                                                    |
   |   │ │ └─TableRowIDScan_42(Probe)         | 1.98    | cop[tikv] | table:A                                                    | keep order:false                                                                                                                                                                                                         |
   |   │ └─IndexReader_45(Probe)              | 2.72    | root      |                                                            | index:IndexRangeScan_44                                                                                                                                                                                                  |
   |   │   └─IndexRangeScan_44                | 2.72    | cop[tikv] | table:C, index:idx_ref_name(ref_name)                      | range:["Rosenthal","Rosentham"), keep order:false                                                                                                                                                                        |
   |   └─TableReader_15(Probe)                | 1.98    | root      |                                                            | data:TableRangeScan_14                                                                                                                                                                                                   |
   |     └─TableRangeScan_14                  | 1.98    | cop[tikv] | table:B                                                    | range: decided by [universe.planetarysystem.df_id], keep order:false                                                                                                                                                     |
   +------------------------------------------+---------+-----------+------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   10 rows in set (0.01 sec)
   ```

+ Which two tables are joined first? What type of table join is used?
  + Answer: Table `planetarySystem` (A) and `parameterReference` (C) are joined first, the join type is `HashJoin`.
+ Which table only participates in the second table join? What type of table join is used?
  + Answer: Table `discoveryFacility` (B), the join type is `IndexJoin`.
+ What is the access method for each table query?
  + Answer: `IndexRangeScan` and `IndexLookUp` for table `planetarySystem` (A); `IndexRangeScan` and `IndexReader` for table `parameterReference` (C); `TableRangeScan` and `TableReader` for table `discoveryFacility` (B).
+ Does this access strategy result in good performance? Why?
  + Answer: Yes. This is because `TableFullScan` or other filtering actions such as SELECT before table joining are avoided by using indexes.

### Step 3. Add indexes and check the execution plan for the following query

   ```sql
   CREATE INDEX idx_ref_id ON planetarySystem(ref_id);
   CREATE INDEX idx_df_id ON planetarySystem(df_id);
   ANALYZE TABLE planetarySystem;
   ```

   ```output
   Query OK, 0 rows affected, 1 warning (4.00 sec)
   ```

   ```sql
   EXPLAIN SELECT A.pl_name, A.hostname, A.discoverymethod, A.disc_year,B.name as DiscoveryFacility, C.ref_name as ParameterReference 
   FROM planetarySystem A, discoveryFacility B, parameterReference C 
   WHERE A.df_id = B.id AND A.ref_id = C.id AND C.ref_name LIKE 'Rosenthal%' AND A.hostname LIKE '14 Her' AND A.disc_year > 2020;
   ```

   ```output
   +------------------------------------------+---------+-----------+------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | id                                       | estRows | task      | access object                                              | operator info                                                                                                                                                                                                            |
   +------------------------------------------+---------+-----------+------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | Projection_13                            | 1.98    | root      |                                                            | universe.planetarysystem.pl_name, universe.planetarysystem.hostname, universe.planetarysystem.discoverymethod, universe.planetarysystem.disc_year, universe.discoveryfacility.name, universe.parameterreference.ref_name |
   | └─IndexJoin_19                           | 1.98    | root      |                                                            | inner join, inner:TableReader_16, outer key:universe.planetarysystem.df_id, inner key:universe.discoveryfacility.id, equal cond:eq(universe.planetarysystem.df_id, universe.discoveryfacility.id)                        |
   |   ├─HashJoin_71(Build)                   | 1.98    | root      |                                                            | inner join, equal:[eq(universe.planetarysystem.ref_id, universe.parameterreference.id)]                                                                                                                                  |
   |   │ ├─IndexLookUp_85(Build)              | 1.98    | root      |                                                            |                                                                                                                                                                                                                          |
   |   │ │ ├─IndexRangeScan_83(Build)         | 1.98    | cop[tikv] | table:A, index:idx_hostname_disc_year(hostname, disc_year) | range:("14 Her" 2020,"14 Her" +inf], keep order:false                                                                                                                                                                    |
   |   │ │ └─TableRowIDScan_84(Probe)         | 1.98    | cop[tikv] | table:A                                                    | keep order:false                                                                                                                                                                                                         |
   |   │ └─IndexReader_87(Probe)              | 2.72    | root      |                                                            | index:IndexRangeScan_86                                                                                                                                                                                                  |
   |   │   └─IndexRangeScan_86                | 2.72    | cop[tikv] | table:C, index:idx_ref_name(ref_name)                      | range:["Rosenthal","Rosentham"), keep order:false                                                                                                                                                                        |
   |   └─TableReader_16(Probe)                | 1.98    | root      |                                                            | data:TableRangeScan_15                                                                                                                                                                                                   |
   |     └─TableRangeScan_15                  | 1.98    | cop[tikv] | table:B                                                    | range: decided by [universe.planetarysystem.df_id], keep order:false                                                                                                                                                     |
   +------------------------------------------+---------+-----------+------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   10 rows in set (0.01 sec)
   ```

+ Has the join order of three tables been changed? Why?
  + Answer: No. Because the optimizer often determines the order of the join based on the amount of data involved in each table, with the table having the smallest amount of data being the first to join. It determine the number of rows in each table that participate in the join based on the filtering conditions in the query, as follows:

   ```sql
   SELECT COUNT(*) FROM planetarysystem WHERE hostname LIKE '14 Her' AND disc_year > 2020;
   SELECT COUNT(*) FROM parameterReference WHERE ref_name LIKE 'Rosenthal%';
   SELECT COUNT(*) FROM discoveryFacility;
   ```

   ```output
   +----------+
   | COUNT(*) |
   +----------+
   |        2 |
   +----------+
   1 row in set (0.01 sec)

   +----------+
   | COUNT(*) |
   +----------+
   |        2 |
   +----------+
   1 row in set (0.00 sec)

   +----------+
   | COUNT(*) |
   +----------+
   |       70 |
   +----------+
   1 row in set (0.00 sec)
   ```

+ The conclusion is that the `planetarySystem` and `parameterReference` tables participates in the join with the fewest rows, while the `discoveryFacility` table participates in the connection with the most rows.

### Step 4. Drop the indexes

   ```sql
   ALTER TABLE planetarySystem DROP INDEX idx_ref_id;
   ALTER TABLE planetarySystem DROP INDEX idx_df_id;
   ALTER TABLE planetarySystem DROP INDEX idx_hostname_disc_year;
   ALTER TABLE ParameterReference DROP INDEX idx_ref_name;
   ```

   ```output
   Query OK, 0 rows affected (0.52 sec)
   ```

### Step 5. Check the execution plan for the following query

   ```sql
   EXPLAIN SELECT * FROM planetarySystem WHERE ref_id IN ( SELECT ID FROM parameterReference WHERE ref_name LIKE 'Rosenthal%');
   ```

   ```output
   tidb>EXPLAIN SELECT * FROM planetarySystem WHERE ref_id IN ( SELECT ID FROM parameterReference WHERE ref_name LIKE 'Rosenthal%');
   +------------------------------+----------+-----------+--------------------------+-----------------------------------------------------------------------------------------+
   | id                           | estRows  | task      | access object            | operator info                                                                           |
   +------------------------------+----------+-----------+--------------------------+-----------------------------------------------------------------------------------------+
   | HashJoin_26                  | 45.97    | root      |                          | inner join, equal:[eq(universe.parameterreference.id, universe.planetarysystem.ref_id)] |
   | ├─TableReader_31(Build)      | 2.72     | root      |                          | data:Selection_30                                                                       |
   | │ └─Selection_30             | 2.72     | cop[tikv] |                          | like(universe.parameterreference.ref_name, "Rosenthal%", 92)                            |
   | │   └─TableFullScan_29       | 2055.00  | cop[tikv] | table:parameterReference | keep order:false                                                                        |
   | └─TableReader_28(Probe)      | 34713.00 | root      |                          | data:TableFullScan_27                                                                   |
   |   └─TableFullScan_27         | 34713.00 | cop[tikv] | table:planetarySystem    | keep order:false                                                                        |
   +------------------------------+----------+-----------+--------------------------+-----------------------------------------------------------------------------------------+
   6 rows in set (0.00 sec)
   ```

+ What is the join type in the join?
  + Answer: `HashJoin`
+ What is the select type for the tables in the join?
  + Answer: `TableFullScan`, `Selection` and `TableReader` for table `parameterReference`; `TableFullScan` and `TableReader` for table `parameterReference`.
+ Does this access strategy result in good performance? Why?
  + Answer: No. First of all, for filtering with the WHERE clause, we can create indexes of table `parameterReference`. Secondly, we can also create indexes on the join columns of table `planetarySystem`.

### Step 6. Add indexes and check the execution plan for the following query

   ```sql
   CREATE INDEX idx_ref_id ON planetarySystem(ref_id);
   CREATE INDEX idx_ref_name ON ParameterReference(ref_name); 
   ANALYZE TABLE planetarySystem;
   ANALYZE TABLE ParameterReference;
   ```

   ```output
   Query OK, 0 rows affected, 1 warning (0.83 sec)
   ```

   ```sql
   EXPLAIN SELECT * FROM planetarySystem WHERE ref_id IN ( SELECT ID FROM parameterReference WHERE ref_name LIKE 'Rosenthal%');
   ```

   ```output
   +----------------------------------+---------+-----------+--------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | id                               | estRows | task      | access object                                          | operator info                                                                                                                                                                                         |
   +----------------------------------+---------+-----------+--------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | IndexHashJoin_20                 | 45.97   | root      |                                                        | inner join, inner:IndexLookUp_17, outer key:universe.parameterreference.id, inner key:universe.planetarysystem.ref_id, equal cond:eq(universe.parameterreference.id, universe.planetarysystem.ref_id) |
   | ├─IndexReader_46(Build)          | 2.72    | root      |                                                        | index:IndexRangeScan_45                                                                                                                                                                               |
   | │ └─IndexRangeScan_45            | 2.72    | cop[tikv] | table:parameterReference, index:idx_ref_name(ref_name) | range:["Rosenthal","Rosentham"), keep order:false                                                                                                                                                     |
   | └─IndexLookUp_17(Probe)          | 45.97   | root      |                                                        |                                                                                                                                                                                                       |
   |   ├─IndexRangeScan_15(Build)     | 45.97   | cop[tikv] | table:planetarySystem, index:idx_ref_id(ref_id)        | range: decided by [eq(universe.planetarysystem.ref_id, universe.parameterreference.id)], keep order:false                                                                                             |
   |   └─TableRowIDScan_16(Probe)     | 45.97   | cop[tikv] | table:planetarySystem                                  | keep order:false                                                                                                                                                                                      |
   +----------------------------------+---------+-----------+--------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   6 rows in set (0.00 sec)
   ```

+ What is the join type in the join?
  + Answer: `IndexHashJoin`
+ What is the select type for the tables in the join?
  + Answer: `IndexRangeScan` and `IndexReader` for table `parameterReference`; `IndexRangeScan` and `IndexLookUp` for table `planetarySystem`.
+ Does this access strategy result in good performance? Why?
  + Answer: Yes. The index `idx_ref_id` ensure that there is an index on the join columns of the `planetarySystem` table, the `HashJoin` can be turned into an `IndexHashJoin`, which avoids full table scans. At the same time, by ensuring that there is an index `idx_ref_name` on the `ParameterReference` table, filtering can be performed first to avoid full table scans.

### Step 7. Drop the indexes

   ```sql
   DROP INDEX idx_ref_id ON planetarysystem;
   DROP INDEX idx_ref_name ON parameterreference;
   ```

   ```output
   Query OK, 0 rows affected (0.52 sec)
   ```
