MySQL | SQLite経験者向け、30日で習得するMySQL:差分理解 - Day6 インデックス基礎

SQL MySQL
スポンサーリンク

Day6 前半のゴール

「“インデックス=検索用の目次”を、感覚レベルで理解する」

SQLiteでもインデックスはありましたが、「なんとなくPRIMARY KEYに勝手についてるやつ」くらいの理解で済ませていたかもしれません。
MySQLでは、データ量が増えたときの性能に直結するので、インデックス設計がかなり重要になります。

Day6 前半のゴールは次の3つです。
インデックスを「検索用の目次」としてイメージできる
CREATE INDEX の基本的な書き方を理解する
複合インデックスが「どんな検索に効いて、どんな検索には効かないか」の方向性をつかむ

ここではまだ「細かい最適化」ではなく、「考え方の土台」を固めます。


インデックスとは何か

「本棚の“目次”があるかないかの違い」

まずはイメージからいきます。

テーブル=本棚
行=本1冊
インデックス=目次・索引

だと思ってください。

インデックスがない場合、
「名前が『山田太郎』のユーザーを探したい」とき、
本棚の本を最初から最後まで全部めくる必要があります。

SQLで言うと、こういうクエリです。

SELECT *
FROM users
WHERE name = '山田太郎';
SQL

インデックスがないと、MySQLは users の全行を1件ずつチェックして、
name が ‘山田太郎’ かどうかを確認します。
これを「フルスキャン(全表走査)」と呼びます。

行数が少ないうちは問題ありませんが、
100万行、1000万行と増えてくると、
毎回これをやるのはさすがに重くなります。

そこで登場するのがインデックスです。
name カラムにインデックスを張っておくと、
「name の値だけを集めた“目次”」が別に作られます。

MySQLはまずその目次を見て、
「’山田太郎’ がどの行にいるか」を素早く特定し、
必要な行だけを取りに行きます。


PRIMARY KEY とインデックスの関係

「PRIMARY KEY には“勝手にインデックスが付く”」

ここで1つ整理しておきたいのが、
PRIMARY KEY とインデックスの関係です。

MySQLでは、PRIMARY KEY を定義すると、
そのカラムには自動的にインデックスが付きます。

CREATE TABLE users (
  id    INT AUTO_INCREMENT PRIMARY KEY,
  name  VARCHAR(50),
  email VARCHAR(255)
);
SQL

この場合、id にはインデックスが付いています。

つまり、

SELECT *
FROM users
WHERE id = 123;
SQL

のようなクエリは、
すでに「id のインデックス」を使って高速に検索されます。

Day6 で扱うのは、
「PRIMARY KEY 以外のカラムにもインデックスを張る」
という話です。

例えば、name や email でよく検索するなら、
そこにもインデックスを付ける価値があります。


CREATE INDEX の基本

「“よく検索に使うカラム”に目次を付ける」

単一カラムのインデックスを張る基本形はこうです。

CREATE INDEX idx_users_name
  ON users (name);
SQL

意味はシンプルで、
「users テーブルの name カラムに idx_users_name という名前のインデックスを作る」です。

ポイントは2つです。

インデックス名は自分で付ける(慣例的に idx_テーブル名_カラム名 など)
ON テーブル名(カラム名)という形で対象を指定する

これで、次のようなクエリが速くなります。

SELECT *
FROM users
WHERE name = '山田太郎';
SQL

MySQLは、name のインデックスを使って、
該当行を素早く見つけられるようになります。

SQLiteでも CREATE INDEX はありましたが、
MySQLでは「データ量が増える前提」で、
より意識的に設計することが多くなります。


インデックスの“効くクエリ・効かないクエリ”のイメージ

「WHERE の条件に使うカラムが候補になる」

インデックスが効くのは、主に次のような場面です。

WHERE で絞り込みに使うカラム
JOIN の ON で使うカラム
ORDER BY や GROUP BY で使うカラム

例えば、次のようなクエリが頻繁に実行されるなら、

SELECT *
FROM users
WHERE email = 'taro@example.com';
SQL

email にインデックスを張る価値があります。

CREATE INDEX idx_users_email
  ON users (email);
SQL

逆に、
ほとんど検索条件に使わないカラムにインデックスを張っても、
あまり意味はありません。

インデックスには「作成・更新コスト」もあるので、
「なんとなく全部に付ける」は逆効果になります。

Day6 前半では、
「WHERE や JOIN によく出てくるカラムが候補」
という感覚だけ持っておけば十分です。


複合インデックスとは何か

「“複数カラムの組み合わせ”に対する目次」

次のテーマが複合インデックスです。
これは、「複数のカラムをセットにしたインデックス」です。

例えば、ログイン履歴テーブルを考えます。

CREATE TABLE login_logs (
  id        INT AUTO_INCREMENT PRIMARY KEY,
  user_id   INT NOT NULL,
  logged_at DATETIME NOT NULL,
  ip        VARCHAR(45) NOT NULL
) ENGINE=InnoDB;
SQL

よくあるクエリはこんな感じです。

特定ユーザーのログイン履歴を、日時の新しい順に取りたい

SELECT *
FROM login_logs
WHERE user_id = 123
ORDER BY logged_at DESC
LIMIT 50;
SQL

この場合、
user_idlogged_at の組み合わせに複合インデックスを張ると効果的です。

CREATE INDEX idx_login_logs_user_date
  ON login_logs (user_id, logged_at);
SQL

これは、

「まず user_id で絞り込み、その中で logged_at の順に並べる」

という検索に強いインデックスです。

単一カラムのインデックスを2つ作る(user_id 用と logged_at 用)よりも、
このクエリに対しては効きが良くなります。


複合インデックスの“左から順に”ルール

「先頭のカラムをどう選ぶかが超重要」

複合インデックスで一番大事なルールがこれです。

「インデックスは左から順にしか効かない」

さっきの例で作ったインデックスは、

CREATE INDEX idx_login_logs_user_date
  ON login_logs (user_id, logged_at);
SQL

このインデックスが効くのは、主に次のようなクエリです。

WHERE user_id = ?
WHERE user_id = ? AND logged_at >= ?

つまり、「user_id から始まる条件」です。

逆に、こういうクエリには効きにくくなります。

SELECT *
FROM login_logs
WHERE logged_at >= '2025-05-01';
SQL

先頭カラムの user_id を条件に使っていないからです。

この「左から順に」という性質があるので、
複合インデックスを設計するときは、

どのカラムで最初に絞り込むことが多いか

をよく考える必要があります。

Day6 前半では、
「複合インデックスは順番が命」ということだけ、
強く意識しておいてください。


SQLite と MySQL のインデックスの“温度差”

「SQLiteでは“おまけ”、MySQLでは“必須の設計要素”」

SQLiteでもインデックスはありましたが、
ローカルファイル・小規模用途が多いこともあり、

「とりあえずPRIMARY KEYがあれば十分」
「遅くなったら考えよう」

くらいの扱いでも何とかなることが多かったと思います。

MySQLは、
サーバー上で複数ユーザー・大量データを扱う前提なので、

インデックス設計=性能設計の中核

になります。

Day6 の段階では、

PRIMARY KEY には自動でインデックスが付く
よく検索に使うカラムには、自分でインデックスを張る
複合インデックスは「左から順に」効くので順番が重要

この3点が押さえられていれば、
次の「実際にEXPLAINで確認する」「どこまで張るかを判断する」に進めます。


Day6 前半のまとめ

インデックスは「テーブルに対する検索用の目次」であり、WHERE や JOIN、ORDER BY でよく使うカラムにインデックスを張ることで、フルスキャンせずに必要な行だけを素早く見つけられるようになる。
PRIMARY KEY には自動的にインデックスが付くため、WHERE id = ? のような検索は最初から高速だが、name や email など他のカラムで頻繁に検索する場合は、CREATE INDEX idx_users_name ON users(name); のように自分でインデックスを追加する必要がある。
複合インデックスは「複数カラムの組み合わせ」に対する目次で、CREATE INDEX idx_login_logs_user_date ON login_logs(user_id, logged_at); のように定義すると、「user_id で絞って logged_at で並べる」クエリに強くなるが、「左から順にしか効かない」という性質があるため、先頭にどのカラムを置くかが非常に重要になる。
SQLiteではインデックスをあまり意識しなくても済む場面が多かったのに対し、MySQLでは「インデックス設計=性能設計」の側面が強く、Day6の時点で“よく検索に使うカラムに目次を付ける”という発想を持てるかどうかが、その後のスケールに大きく影響する。

後半では、
実際にインデックスあり/なしでクエリを投げたときの違いや、
複合インデックスの「効くパターン・効かないパターン」を、
具体的なSQL例とともにもう一段深く見ていきます。

タイトルとURLをコピーしました