## モジュール 4: パフォーマンス比較

+ この演習では、パーティションテーブルがパフォーマンスに与える影響を評価します。
  + 既存のテーブルをLIST COLUMNパーティションに分割します。
  + 元のテーブルとパーティションテーブルの両方に対してクエリを実行し、パフォーマンスを比較します。

### 手順 1. 非パーティションテーブル `orders_no_partition` を作成する

  ```sql
  CREATE TABLE orders_no_partition(
     ID int(11) NOT NULL AUTO_INCREMENT,
     Name char(35) NOT NULL DEFAULT '',
     StoreCode char(3) NOT NULL DEFAULT '',
     City char(35) NOT NULL DEFAULT '',
     Mount int(11) NOT NULL DEFAULT '0',
     Type int(11) NOT NULL DEFAULT '0',
     PRIMARY KEY(ID),
     Key idx_StoreCode(StoreCode)
  );
  ```

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

  ```sql
  EXIT
  ```

  ```bash
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 1);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 2);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 3);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 4);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 5);"; done;
  for i in `seq 9`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO  test.orders_no_partition SELECT NULL, Name, StoreCode, City,Mount, Type FROM test.orders_no_partition;"; done;
  ```

  ```output
  mysql: [Warning] Using a password on the command line interface can be insecure.
  ... ...
  mysql: [Warning] Using a password on the command line interface can be insecure.
  ```

### 手順 2. LIST COLUMNパーティションテーブル `orders_partition` を作成する

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```sql
  use test;
  ```

  ```sql
  CREATE TABLE orders_partition(
     ID int(11) NOT NULL AUTO_INCREMENT,
     Name char(35) NOT NULL DEFAULT '',
     StoreCode char(3) NOT NULL DEFAULT '',
     City char(35) NOT NULL DEFAULT '',
     Mount int(11) NOT NULL DEFAULT '0',
     Type int(11) NOT NULL DEFAULT '0',
     PRIMARY KEY(ID, Type),
     Key idx_StoreCode(StoreCode)
     )
     PARTITION BY LIST COLUMNS (Type)(
        PARTITION p0 VALUES IN (1),
        PARTITION p1 VALUES IN (2),
        PARTITION p2 VALUES IN (3),
        PARTITION p3 VALUES IN (4),
        PARTITION p4 VALUES IN (5)
  );
  ```

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

  ```sql
  EXIT
  ```

  ```bash
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 1);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 2);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 3);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 4);"; done;
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 5);"; done;
  for i in `seq 9`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP} -e "INSERT INTO test.orders_partition SELECT NULL, Name, StoreCode, City,Mount, Type from test.orders_partition;"; done;
  ```

  ```output
  for i in `seq 1000`; do mysql -uroot -P4000 -h${HOST_DB1_PRIVATE_IP}  -e "INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 1);"; done;
  mysql: [Warning] Using a password on the command line interface can be insecure.
  ... ...
  mysql: [Warning] Using a password on the command line interface can be insecure.
  ```

### 手順 3. `orders_no_partition` と `orders_partition` にデータを挿入する

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```sql
  use test;
  ```

  ```output
  Database changed
  ```

  ```sql
  INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 6);
  INSERT INTO test.orders_no_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 6);
  INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 6);
  ```

  ```output
  tidb>INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 6);
  ERROR 1526 (HY000): Table has no partition for value from column_list
  ```

+ エラー `ERROR 1526 (HY000): Table has no partition for value from column_list` の理由は、テーブル `orders_partition` に、`Type 6` のパーティションが存在しないためです。よって、パーティションを追加する必要があります。

   ```sql
  ALTER TABLE test.orders_partition ADD PARTITION (PARTITION p5 VALUES IN (6));
  INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 6);
  INSERT INTO test.orders_partition VALUES(NULL,SUBSTRING(MD5(RAND()),1,20), SUBSTRING(MD5(RAND()),1,3), SUBSTRING(MD5(RAND()),1,20), FLOOR(RAND()*10000), 6);
  ```

  ```output
  Query OK, 1 row affected (0.00 sec)
  ```

### 手順 4. `orders_no_partition` テーブルの状態を確認する

+ 出力の中で `Create_options` カラムに含まれる値は何でしょうか？

   ```sql
  SHOW TABLE STATUS LIKE 'orders_no_partition'\G
  ```

  ```output
  tidb>SHOW TABLE STATUS LIKE 'orders_no_partition'\G
  *************************** 1. row ***************************
             Name: orders_no_partition
           Engine: InnoDB
          Version: 10
       Row_format: Compact
             Rows: 2560000
   Avg_row_length: 70
      Data_length: 179200000
  Max_data_length: 0
     Index_length: 10240000
        Data_free: 0
   Auto_increment: 2560002
      Create_time: 2023-11-16 15:10:53
      Update_time: NULL
       Check_time: NULL
        Collation: utf8mb4_bin
         Checksum:
   Create_options:
          Comment:
  1 row in set (0.01 sec)
  ```

+ 回答: `orders_no_partition` テーブルの `SHOW TABLE STATUS` 出力における `Create_options` カラムは空です。

### 手順 5. `orders_partition` テーブルの状態を確認する

+ 出力の中で `Create_options` カラムに含まれる値は何でしょうか？

   ```sql
  SHOW TABLE STATUS LIKE 'orders_partition'\G
  ```

  ```output
  tidb>SHOW TABLE STATUS LIKE 'orders_partition'\G
  *************************** 1. row ***************************
             Name: orders_partition
           Engine: InnoDB
          Version: 10
       Row_format: Compact
             Rows: 2560002
   Avg_row_length: 70
      Data_length: 179200140
  Max_data_length: 0
     Index_length: 51200040
        Data_free: 0
   Auto_increment: 2560004
      Create_time: 2023-11-16 15:48:21
      Update_time: NULL
       Check_time: NULL
        Collation: utf8mb4_bin
         Checksum:
   Create_options: partitioned
          Comment:
  1 row in set (0.01 sec)
  ```

+ 回答: `orders_partition` テーブルの `SHOW TABLE STATUS` 出力における `Create_options` カラムは partitioned です。

### 手順 6. 2つのテーブルの `CREATE TABLE` ステートメントを比較する

+ `orders_partition` テーブルはどのようにパーティションされていますか？

   ```sql
  SHOW CREATE TABLE orders_no_partition\G
  SHOW CREATE TABLE orders_partition\G
  ```

  ```output
  *************************** 1. row ***************************
        Table: orders_no_partition
  Create Table: CREATE TABLE `orders_no_partition` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(35) NOT NULL DEFAULT '',
  `StoreCode` char(3) NOT NULL DEFAULT '',
  `City` char(35) NOT NULL DEFAULT '',
  `Mount` int(11) NOT NULL DEFAULT '0',
  `Type` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`ID`) /*T![clustered_index] CLUSTERED */,
  KEY `idx_StoreCode` (`StoreCode`)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=3182134
  1 row in set (0.01 sec)

  *************************** 1. row ***************************
        Table: orders_partition
  Create Table: CREATE TABLE `orders_partition` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(35) NOT NULL DEFAULT '',
  `StoreCode` char(3) NOT NULL DEFAULT '',
  `City` char(35) NOT NULL DEFAULT '',
  `Mount` int(11) NOT NULL DEFAULT '0',
  `Type` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`ID`,`Type`) /*T![clustered_index] CLUSTERED */,
  KEY `idx_StoreCode` (`StoreCode`)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=2677458
  PARTITION BY LIST COLUMNS(`Type`)
  (PARTITION `p0` VALUES IN (1),
  PARTITION `p1` VALUES IN (2),
  PARTITION `p2` VALUES IN (3),
  PARTITION `p3` VALUES IN (4),
  PARTITION `p4` VALUES IN (5),
  PARTITION `p5` VALUES IN (6))
  1 row in set (0.00 sec)
  ```

+ 回答: `orders_partition` テーブルは `Type` カラムの値に基づいてLIST COLUMNS パーティショニングを使用しています。`Type` カラムでのパーティショニングをサポートするために、`Type` カラムはプライマリキーの一部である必要があります。

### 手順 7. `orders_partition` テーブルの各パーティションの行数を確認する

+ `INFORMATION_SCHEMA` データベース内の `PARTITIONS` テーブルをクエリして、orders_partition` テーブルの各パーティションの行数およびデータ長を取得します。

   ```sql
  ANALYZE TABLE orders_partition;
  SELECT PARTITION_NAME, TABLE_ROWS, DATA_LENGTH FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='orders_partition';
  ```

  ```output
  +----------------+------------+-------------+
  | PARTITION_NAME | TABLE_ROWS | DATA_LENGTH |
  +----------------+------------+-------------+
  | p0             |     512000 |    35840000 |
  | p1             |     512000 |    35840000 |
  | p2             |     512000 |    35840000 |
  | p3             |     512000 |    35840000 |
  | p4             |     512000 |    35840000 |
  | p5             |          2 |         140 |
  +----------------+------------+-------------+
  6 rows in set (0.01 sec)
  ```

### 手順 8. パフォーマンスを比較する

  ```sql
  SELECT * FROM test.orders_no_partition WHERE Type = 6;
  SELECT * FROM test.orders_partition WHERE Type = 6;
  ```

  ```output
  +---------+----------------------+-----------+----------------------+-------+------+
  | ID      | Name                 | StoreCode | City                 | Mount | Type |
  +---------+----------------------+-----------+----------------------+-------+------+
  | 2560001 | 71add0d7809cbcbcbe70 | 5a7       | ed366dcc516557dc9dd1 |  9718 |    6 |
  | 2560002 | 729981de40b9d6518610 | ce2       | ec29e10a4e200a85f39d |  2252 |    6 |
  +---------+----------------------+-----------+----------------------+-------+------+
  2 rows in set (0.74 sec)

  +---------+----------------------+-----------+----------------------+-------+------+
  | ID      | Name                 | StoreCode | City                 | Mount | Type |
  +---------+----------------------+-----------+----------------------+-------+------+
  | 2560002 | 74daf6785e90d21e49fa | d0d       | 7264824fb92dff278fc8 |  6453 |    6 |
  | 2560003 | 34558dcfaf8c36730107 | d79       | 712aa60533e92f925232 |    14 |    6 |
  +---------+----------------------+-----------+----------------------+-------+------+
  2 rows in set (0.01 sec)
  ```

+ 注：クエリをもう一度実行すると、キャッシュされたデータを選択したため、両者のパフォーマンスに違いがないことがわかります。

  ```output
  tidb>SELECT * FROM test.orders_no_partition WHERE Type = 6;
  +---------+----------------------+-----------+----------------------+-------+------+
  | ID      | Name                 | StoreCode | City                 | Mount | Type |
  +---------+----------------------+-----------+----------------------+-------+------+
  | 2560001 | 71add0d7809cbcbcbe70 | 5a7       | ed366dcc516557dc9dd1 |  9718 |    6 |
  | 2560002 | 729981de40b9d6518610 | ce2       | ec29e10a4e200a85f39d |  2252 |    6 |
  +---------+----------------------+-----------+----------------------+-------+------+
  1 row in set (0.00 sec)

  tidb>SELECT * FROM test.orders_partition WHERE Type = 6;
  +---------+----------------------+-----------+----------------------+-------+------+
  | ID      | Name                 | StoreCode | City                 | Mount | Type |
  +---------+----------------------+-----------+----------------------+-------+------+
  | 2560002 | 74daf6785e90d21e49fa | d0d       | 7264824fb92dff278fc8 |  6453 |    6 |
  | 2560003 | 34558dcfaf8c36730107 | d79       | 712aa60533e92f925232 |    14 |    6 |
  +---------+----------------------+-----------+----------------------+-------+------+
  2 rows in set (0.00 sec)
  ```

+ あなたの結果に基づいて、パーティショニングがクエリのパフォーマンスに与える影響は何ですか？
+ 回答: 単一のパーティションからデータを使用するクエリの場合、非パーティションテーブルから同じデータにアクセスするよりもパフォーマンスが良いです。しかし、すべてのパーティションにアクセスする必要があるクエリの場合、パフォーマンスは非パーティションテーブルから同じデータにアクセスするよりも劣ることがあります。

### 手順 9. `orders_no_partition` と `orders_partition` テーブルを削除する

  ```sql
  DROP TABLE test.orders_no_partition;
  DROP TABLE test.orders_partition;
  ```

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