SQLインデックス(索引)入門!パフォーマンス改善でデータベースを高速化する初心者ガイド
生徒
「データベースからデータを取り出すときに、時間がかかってしまうことがあります。もっと速く検索する方法はないんですか?」
先生
「それには『インデックス』という仕組みを使います。本で言えば『目次』や『索引』のようなものです。これがあるだけで、探し物のスピードが劇的に変わるんですよ。」
生徒
「目次ですか!それならイメージしやすいです。でも、どんなときでも目次を使えば速くなるんですか?」
先生
「実は、目次がうまく機能しないパターンもあるんです。今日は、インデックスが効く場合と効かない場合の違いについて、具体例を交えてじっくり解説していきましょう。」
1. SQLとは何か?
SQL(エスキューエル)は、データベースと呼ばれる「大量のデータを整理して保存する箱」に対して指示を出すための言語です。 例えば、オンラインショッピングの会員名簿の中から特定の人を探したり、銀行のシステムで残高を確認したり、新しい注文情報を追加したりするときに使います。
プログラミング未経験の方にとっては、巨大なExcelシートを操作するための専用の「命令文」だと考えるとわかりやすいでしょう。 しかし、データが数百万件、数千万件と増えてくると、ただ命令を出すだけではコンピュータも処理に時間がかかってしまいます。そこで重要になるのが「パフォーマンス(処理速度)」と「最適化(効率化)」という考え方です。
2. インデックス(索引)の仕組みを理解しよう
データベースにおけるインデックスとは、特定のデータが「どこにあるか」を記録した辞書のようなものです。
想像してみてください。1,000ページある分厚い辞書から「リンゴ」という言葉を探すとします。 もし目次も索引もなかったら、あなたは1ページ目から順番に最後までめくって探さなければなりません。これをデータベースの世界では「フルスキャン(全件検索)」と呼び、非常に効率が悪いです。
しかし、巻末の「索引」で「ら行」を見れば、すぐに「リンゴは500ページにある」とわかります。これがインデックスの効果です。 SQLのパフォーマンスを上げるためには、よく検索する項目(名前やメールアドレスなど)にこのインデックスを貼っておくことが不可欠です。
3. インデックスが効果を発揮する場合(効く例)
まずは、インデックスが正しく機能して、検索が高速化される具体的なケースを見ていきましょう。 最も基本的なのは、特定の値を「=(イコール)」で探す場合です。
パターンA:特定のIDで検索する
ユーザーIDのように、一人ひとりに割り振られた番号で検索するとき、インデックスは最強の力を発揮します。
【検索前のテーブル:users】
id | name | age | city
---+-----------+-----+---------
1 | 田中太郎 | 25 | 東京都
2 | 佐藤花子 | 19 | 大阪府
3 | 鈴木一郎 | 30 | 愛知県
4 | 高橋次郎 | 45 | 福岡県
5 | 伊藤純子 | 22 | 北海道
6 | 渡辺健太 | 33 | 東京都
SELECT *
FROM users
WHERE id = 4;
【実行結果】
id | name | age | city
---+-----------+-----+---------
4 | 高橋次郎 | 45 | 福岡県
このように「IDが4の人を連れてきて!」という命令に対し、インデックスがあればデータベースは迷わず4番のデータにアクセスします。
パターンB:前方一致の検索
文字の検索でも、インデックスは効きます。ただし条件があります。それは「最初の方がわかっていること(前方一致)」です。 例えば「名字が『佐』で始まる人を検索する」といった場合です。
SELECT *
FROM users
WHERE name LIKE '佐%';
※「LIKE '佐%'」の「%」は「その後に何が続いてもOK」という意味の記号です。
【実行結果】
id | name | age | city
---+-----------+-----+---------
2 | 佐藤花子 | 19 | 大阪府
辞書を引くときも「さ」の項目から探せばいいので、インデックスが有効に働きます。
4. インデックスが効かない場合(効かない例)
初心者が最も陥りやすい罠が、インデックスを貼っているのに「書き方一つで魔法が解けてしまう」ことです。 せっかく作った目次を使わずに、わざわざ1ページ目から探し始めてしまう残念なケースを紹介します。
パターンC:後方一致・部分一致の検索
「名前の最後が『子』で終わる人」や「名前に『藤』が含まれる人」を探そうとすると、インデックスは効かなくなります。
SELECT *
FROM users
WHERE name LIKE '%子';
辞書で「最後が『子』で終わる言葉」を探そうと思ったら、結局全部の単語の末尾をチェックしなければなりませんよね? これではインデックス(目次)の意味がなくなり、動作が重くなってしまいます。
パターンD:列に対して計算や加工をしている
これもよくあるミスです。例えば「年齢が20歳未満」を探したいときに、あえて難しい書き方をしてしまうケースです。
-- インデックスが効かない書き方の例
SELECT *
FROM users
WHERE age + 10 < 30;
コンピュータに対して「年齢に10を足した結果が30より小さい人」という命令を出すと、データベースは全てのデータに対して「足し算」という計算作業を行ってから比較を始めます。 インデックスには「元の年齢」の順番でしか記録されていないため、計算が入ると目次が役に立たなくなってしまうのです。
これを解決するには、次のように「列(age)」をそのままの形で使う必要があります。
-- インデックスが効く正しい書き方
SELECT *
FROM users
WHERE age < 20;
5. なぜインデックスを貼りすぎるとダメなのか?
「そんなに便利なら、全部の項目にインデックスを貼ればいいじゃないか」と思うかもしれません。 しかし、それには大きな落とし穴があります。
インデックスは、データが「追加」や「更新」されるたびに、目次も一緒に作り直さなければなりません。 本の内容を書き換えるたびに、索引のページ番号を修正する作業を想像してください。 インデックスが多すぎると、データを保存する作業そのものが非常に遅くなってしまいます。
また、インデックス自体も保存場所(ディスク容量)を消費します。 何でもかんでもインデックスを貼るのではなく、「検索でよく使われる」「データ量が多い」「値の種類が多い(性別のように『男・女』だけではないもの)」といったポイントを絞って設定することが、データベースのプロフェッショナルへの第一歩です。
6. 実際のデータでパフォーマンスを意識してみよう
最後に、複数の条件を組み合わせた時の例を見てみましょう。 例えば、「東京都に住んでいる25歳以上のユーザー」を抽出する場合です。
【検索前のテーブル:users】
id | name | age | city
---+-----------+-----+---------
1 | 田中太郎 | 25 | 東京都
2 | 佐藤花子 | 19 | 大阪府
3 | 鈴木一郎 | 30 | 愛知県
4 | 高橋次郎 | 45 | 福岡県
5 | 伊藤純子 | 22 | 北海道
6 | 渡辺健太 | 33 | 東京都
7 | 小林直樹 | 28 | 東京都
SELECT name, city
FROM users
WHERE city = '東京都' AND age >= 25;
【実行結果】
name | city
----------+---------
田中太郎 | 東京都
渡辺健太 | 東京都
小林直樹 | 東京都
この場合、「city」と「age」の両方にインデックスが貼られていれば、データベースは効率よく候補を絞り込むことができます。 逆にどちらにも貼られていなければ、1件ずつ東京都かどうかを確認し、さらに年齢を確認するという途方もない作業になります。