## モジュール 1: MySQL 互換性

![demo_203-tidb-x-mysql-campat-ja.jpg 85%](https://lab-static.pingcap.com/images/2025/12/2/031e23aab1b1f28b6db167087931979d5789fa6b.jpg)

このモジュールでは、TiDB に接続し、テーブルを作成し、データをインポートした後、ウィンドウ関数など MySQL 8.x の機能や、ストアドプロシージャのような未サポート機能の動作も確認します。詳細な互換性マトリクスは[こちら](https://docs.pingcap.com/ja/tidb/stable/mysql-compatibility/)をご覧ください。

### 手順 1. 割り当てられた Ubuntu 仮想マシンへログイン

- [ターミナル 1](ssh://monitor1-ssh#1) をクリックして演習用に割り当てられた Ubuntu 仮想マシンに接続します。この端末で TiDB Cloud Starter クラスタにアクセスします。

### 手順 2. TiDB Cloud クラスタへの接続確認

- 通常の MySQL データベース同様、TiDB に接続できます。次の 3 つの方法で接続を試みてください。（MySQL クライアントを終了するには `EXIT` を使用します）

- **A. SSL 付き接続:** 次のコマンドで接続します。

  ```bash
  mysql \
   --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
   -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt
  ```

  ```output
  mysql>
  ```

  ```sql
  EXIT;
  ```

- **B. SSL 付き接続（デフォルト）:** `--ssl-mode` オプションを指定しない場合、デフォルトは `PREFERRED` となり、まず SSL 接続を試み、失敗した場合は暗号化なしとなります。

  ```bash
  mysql \
   --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
   -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT}
  ```

  ```output
  mysql>
  ```

  ```sql
  EXIT;
  ```

- **C. SSL なし接続:** `--ssl-mode=DISABLED` に変更します。接続エラーが発生します。

  ```bash
  mysql \
   --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
   -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
   --ssl-mode=DISABLED
  ```

  ```output
  mysql: [Warning] Using a password on the command line interface can be insecure.
  ERROR 1105 (HY000): Connections using insecure transport are prohibited. See https://docs.tidb.io/tidbcloud/secure-connections-to-serverless-tier-clusters
  ```

> [!NOTE]
> 本ハンズオンではパブリックエンドポイントを利用しますが、TiDB Cloud Starter はプライベートネットワークエンドポイントもサポートしており、実運用シナリオでは推奨され一般的です。

- TiDB Cloud コンソールの[クラスタのページ](https://tidbcloud.com/project/clusters)にアクセスし、演習クラスタ名をクリックします。`新しいクラスタを作成`で演習を開始した場合、クラスタ名は `${LAB:SERVERLESS_CLUSTER_NAME}` です。

![choose-starter-cluster-v2.jpg 70%](https://lab-static.pingcap.com/images/2025/11/24/968052d742cea1bce34cda07961dbffb8d309ebd.jpg)

- `Connect（接続）`をクリックします。

![starter-connect.jpg 80%](https://lab-static.pingcap.com/images/2025/11/18/dfb8f70aacdf6b738e74976a2d292f67a5417314.jpg)

- `Connection Type` 内に `Private Endpoint` が表示されることを確認できます。

![starter-connect-methods.jpg 70%](https://lab-static.pingcap.com/images/2025/11/18/724569d619c6850877608947c662edff50462206.jpg)

- ワークショップ終了後、このページからプライベートエンドポイントを有効化できます。これにより、組織の実ネットワーク（例: VPC）からクラスタへアクセスできます。今回は設定せず次のステップへ進みます。

### 手順 3. 基本機能の確認

- [ターミナル 1](ssh://monitor1-ssh#1) で TiDB に接続します。

  ```bash
  mysql \
   --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
   -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt
  ```

  ```output
  mysql>
  ```

- `${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}` という新しいデータベースを作成します。MySQL と同様に TiDB を利用します。

  ```sql
  DROP DATABASE IF EXISTS ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};
  CREATE DATABASE ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};
  SHOW DATABASES\G
  ```

  ```output
  *************************** 1. row ***************************
  Database: INFORMATION_SCHEMA
  *************************** 2. row ***************************
  Database: PERFORMANCE_SCHEMA
  *************************** 3. row ***************************
  Database: ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  *************************** 4. row ***************************
  Database: mysql
  *************************** 5. row ***************************
  Database: test
  5 rows in set (0.02 sec)
  ```

- シンプルなテーブルを作成します。MySQL と同じように TiDB を使います。

  ```sql
  USE ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};
  CREATE TABLE color (id INT AUTO_INCREMENT, name TEXT, Point INT, PRIMARY KEY (id));
  INSERT INTO color (name, Point) VALUES ('red', 1);
  INSERT INTO color (name, Point) VALUES ('orange', 1);
  INSERT INTO color (name, Point) VALUES ('yellow', 2);
  SELECT * FROM color;
  ```

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

  Query OK, 1 row affected (0.04 sec)

  Query OK, 1 row affected (0.01 sec)

  Query OK, 1 row affected (0.01 sec)

  +----+--------+-------+
  | id | name   | Point |
  +----+--------+-------+
  |  1 | red    |     1 |
  |  2 | orange |     1 |
  |  3 | yellow |     2 |
  +----+--------+-------+
  3 rows in set (0.01 sec)
  ```

- デフォルトの文字セットと照合順序（コレーション）を確認します。

  ```sql
  SHOW CREATE TABLE color\G
  ```

  ```output
  *************************** 1. row ***************************
         Table: color
  Create Table: CREATE TABLE `color` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` text DEFAULT NULL,
    `Point` int(11) DEFAULT NULL,
    PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=30001
  1 row in set (0.00 sec)
  ```

- ウィンドウ関数の例。

  ```sql
  SELECT
    id,
    name,
    Point,
    RANK() OVER w as 'rank'
  FROM color
  WINDOW w AS (ORDER BY Point);
  ```

  ```output
  +----+--------+-------+------+
  | id | name   | Point | rank |
  +----+--------+-------+------+
  |  1 | red    |     1 |    1 |
  |  2 | orange |     1 |    1 |
  |  3 | yellow |     2 |    3 |
  +----+--------+-------+------+
  3 rows in set (0.01 sec)
  ```

- TiDB は MySQL との高い互換性を持ちますが、ストアドプロシージャなど一部の機能は未対応です。以下のストアドプロシージャ作成は失敗します。

  ```sql
  delimiter //
  CREATE PROCEDURE sample()
  BEGIN
  SELECT * FROM color;
  END //
  delimiter ;
  ```

  ```output
  ERROR 8108 (HY000): Unsupported type *ast.ProcedureInfo
  ```

- ここまでで TiDB の接続・利用イメージがつかめたかと思います。次のステップでより実践的なサンプルスキーマを作成しましょう。

### 手順 4. 書店サンプルテーブルの作成

- [ターミナル 1](ssh://monitor1-ssh#1)で、次のコマンドを使って提供された SQL スクリプトの内容を簡単に確認します。このスクリプトはサンプル書店アプリのために 6 つのテーブルを作成しデータも投入します。

  ```sql
  SYSTEM cat create_bookshop_schema_in_current_db.sql | egrep '(CREATE TABLE|INSERT INTO)';
  ```

  ```output
  CREATE TABLE `authors` (
  INSERT INTO `authors`
  CREATE TABLE `book_authors` (
  INSERT INTO `book_authors`
  CREATE TABLE `books` (
  INSERT INTO `books`
  CREATE TABLE `orders` (
  CREATE TABLE `ratings` (
  INSERT INTO `ratings`
  CREATE TABLE `users` (
  INSERT INTO `users`
  ```

- 次にこのスクリプトを実行し、`${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}` データベースにデータセットを作成・インポートします。

  ```sql
  USE ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};
  SOURCE create_bookshop_schema_in_current_db.sql
  ```

  ```output
  Query OK, 0 rows affected (0.01 sec)
  ...
  Query OK, 0 rows affected (0.01 sec)
  ```

### 手順 5. サンプルデータのクエリ

- 1つのクエリで、ジャンルごとの高評価書籍の傾向を分析し、各ジャンル内で上位3冊と、在庫状況を一覧します。以下の複雑なクエリは見やすさに配慮していませんが、TiDBがJOIN, GROUP BY, ORDER BY, 集計関数, ウィンドウ関数, サブクエリなど高機能SQLを1ステートメントでサポートできることを示します。

  ```sql
  SELECT b.type, COUNT(b.id) AS book_count, AVG(r.score) avg_score, v.top_3_rated_title
  FROM books b JOIN ratings r ON b.id = r.book_id JOIN
  (SELECT ov.type, GROUP_CONCAT(ov.title) top_3_rated_title FROM
    (SELECT iv.type, iv.title, RANK() OVER (PARTITION BY iv.type ORDER BY book_avg_score DESC) rank_in_type FROM
      (SELECT books.type, books.title, AVG(ratings.score) as book_avg_score FROM books as books JOIN ratings as ratings ON books.id = ratings.book_id GROUP BY books.type, books.title) iv
    ) ov
    WHERE rank_in_type < 4
    GROUP BY ov.type
  ) v ON v.type = b.type
  GROUP BY type, top_3_rated_title
  ORDER BY avg_score DESC;
  ```

  ```output
  +------------------------------+------------+-----------+------------------------------------------------------------------------------------+
  | type                         | book_count | avg_score | top_3_rated_title                                                                  |
  +------------------------------+------------+-----------+------------------------------------------------------------------------------------+
  | Comics                       |         64 |    3.5156 | Mrs Robinson,Be My Baby,On the Atchison                                            |
  | Arts                         |         86 |    3.4535 | Give Me Everything,Georgia On My Mind,Besame Mucho                                 |
  | Magazine                     |         59 |    3.3051 | Wishing Well,Stayin' Alive,Spanish Harlem                                          |
  | Kids                         |         48 |    3.1875 | What Goes Around Comes Around,Gonna Make You Sweat (Everybody Dance Now),Hey There |
  | Life                         |        175 |    3.1657 | Play That Funky Music,Open Arms,Every Breath You Take                              |
  | Sports                       |         81 |    3.1605 | Waterfalls,The Hustle,My Sweet Lord                                                |
  | Education & Reference        |         65 |    3.1231 | Greatest Love of All,Addicted to Love,ABC                                          |
  | Science & Technology         |        149 |    3.0671 | Build Me Up Buttercup,Dilemma,Daydream Believer                                    |
  | Humanities & Social Sciences |         78 |    2.9615 | Higher Love,12th Street Rag,Body & Soul                                            |
  | Novel                        |         44 |    2.9318 | The Power of Love,Thrift Shop,Summer in the City                                   |
  +------------------------------+------------+-----------+------------------------------------------------------------------------------------+
  10 rows in set (0.01 sec)
  ```

- クエリ内容は深く気にせず、次へ進んでください。

### 手順 6. Python でサンプルデータをクエリ

- OSプロンプトへ戻ります。

  ```sql
  EXIT;
  ```

- Python 仮想環境を作成します。

  ```bash
  python3 -m venv ~/.venv
  ~/.venv/bin/pip install mysql-connector-python prettytable dotenv
  ```

- 分析クエリ用に `top3books.py` という Python スクリプトを作成します。`mysql.connector` モジュールを利用して、MySQLデータベースのように TiDB に接続します。

  ```bash
  cat > top3books.py <<'EOF'
  import mysql.connector
  import datetime
  from prettytable import PrettyTable

  config = {
    'user': '${LAB:SERVERLESS_CLUSTER_USERNAME}',
    'password': '${LAB:SERVERLESS_CLUSTER_PASSWORD}',
    'host': '${LAB:SERVERLESS_CLUSTER_HOST}',
    'database': '${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}',
    'raise_on_warnings': False,
    'port': ${LAB:SERVERLESS_CLUSTER_PORT},
  }

  def execQuery(query, queryName):
    print(queryName)
    print("Query execution start time: ", datetime.datetime.now().strftime("%H:%M:%S.%f"))
    # Executing the query
    cursor.execute(query)
    print("Query execution end time: ", datetime.datetime.now().strftime("%H:%M:%S.%f"))

    # Fetching all rows from the last executed query
    results = cursor.fetchall()

    # Creating a PrettyTable object
    my_table = PrettyTable()

    # Adding field names (column names) to the table
    field_names = [i[0] for i in cursor.description]
    my_table.field_names = field_names

    # Adding rows to the table
    for row in results:
        my_table.add_row(row)

    # Printing the table
    print(my_table)
    print("==========================")

  try:
    # Establishing the connection
    conn = mysql.connector.connect(**config)

    # Creating a cursor object
    cursor = conn.cursor()

    # Your SQL query
    queryName= "■■■Query: Top three books within each genre■■■"
    query = """
    SELECT b.type, COUNT(b.id) AS book_count, AVG(r.score) avg_score, v.top_3_rated_title
    FROM books b JOIN ratings r ON b.id = r.book_id JOIN
    (SELECT ov.type, GROUP_CONCAT(ov.title) top_3_rated_title FROM
      (SELECT iv.type, iv.title, RANK() OVER (PARTITION BY iv.type ORDER BY book_avg_score DESC) rank_in_type FROM
        (SELECT books.type, books.title, AVG(ratings.score) as book_avg_score FROM books as books JOIN ratings as ratings ON books.id = ratings.book_id GROUP BY books.type, books.title) iv
      ) ov
      WHERE rank_in_type < 4
      GROUP BY ov.type
    ) v ON v.type = b.type
    GROUP BY type, top_3_rated_title
    ORDER BY avg_score DESC;
    """
    execQuery(query, queryName)

  except mysql.connector.Error as err:
    print(f"Error: {err}")

  finally:
    # Closing the connection
    if conn.is_connected():
        cursor.close()
        conn.close()
        print("MySQL connection is closed")
  EOF
  ls -l top3books.py
  ```

  ```output
  -rw-r--r--. 1 ubuntu ubuntu 3636 Apr 28 02:22 top3books.py
  ```

- Python スクリプトを実行してレポートを取得します。

  ```bash
  ~/.venv/bin/python top3books.py
  ```

  ```output
  ■■■Query: Top three books within each genre■■■
  Query execution start time:  14:05:18.613932
  Query execution end time:  14:05:18.629749
  +------------------------------+------------+-----------+------------------------------------------------------------------------------------+
  |             type             | book_count | avg_score |                                 top_3_rated_title                                  |
  +------------------------------+------------+-----------+------------------------------------------------------------------------------------+
  |            Comics            |     64     |   3.5156  |                      Mrs Robinson,On the Atchison,Be My Baby                       |
  |             Arts             |     86     |   3.4535  |                 Give Me Everything,Georgia On My Mind,Besame Mucho                 |
  |           Magazine           |     59     |   3.3051  |                     Wishing Well,Stayin' Alive,Spanish Harlem                      |
  |             Kids             |     48     |   3.1875  | What Goes Around Comes Around,Gonna Make You Sweat (Everybody Dance Now),Hey There |
  |             Life             |    175     |   3.1657  |               Play That Funky Music,Open Arms,Every Breath You Take                |
  |            Sports            |     81     |   3.1605  |                        Waterfalls,The Hustle,My Sweet Lord                         |
  |    Education & Reference     |     65     |   3.1231  |                     Greatest Love of All,Addicted to Love,ABC                      |
  |     Science & Technology     |    149     |   3.0671  |                  Build Me Up Buttercup,Dilemma,Daydream Believer                   |
  | Humanities & Social Sciences |     78     |   2.9615  |                      Higher Love,12th Street Rag,Body & Soul                       |
  |            Novel             |     44     |   2.9318  |                  The Power of Love,Thrift Shop,Summer in the City                  |
  +------------------------------+------------+-----------+------------------------------------------------------------------------------------+
  ==========================
  MySQL connection is closed
  ```

- このモジュールでは、SQL・Python いずれでも TiDB を MySQL と同じように利用できることを確認しました（互換性差異は一部を除く）。TiDB へのその他のドライバやORMによるアクセス方法などの詳細は[ドキュメント](https://docs.pingcap.com/tidbcloud/dev-guide-choose-driver-or-orm/)や[関連演習](/labs?target=Developer)も参照してください。次のモジュールへ進みましょう。
