インデックスは「検索を速くする目次」
データベースのインデックスは、本の索引に似ています。索引があれば、特定のキーワードが載っているページを最初から最後まで探さずに見つけられます。
テーブルでも同じです。インデックスがない場合、データベースは条件に合う行を探すためにテーブル全体を読むことがあります。これを全件スキャンと呼びます。
SELECT *
FROM orders
WHERE user_id = 123;
orders.user_id にインデックスがなければ、注文が100件のうちは問題になりません。しかし100万件になると、毎回100万件を確認する可能性があります。
CREATE INDEX idx_orders_user_id ON orders(user_id);
インデックスがあると、データベースは user_id = 123 の行へ効率よくたどり着けます。
初心者がまず見るべき3つの条件
インデックスを考えるときは、いきなり高度なチューニングへ進まず、まず次の3点を確認します。
| 観点 | 確認すること | 例 |
|---|---|---|
| 絞り込み | WHERE でよく使う列か | user_id, status, created_at |
| 並び替え | ORDER BY でよく使う列か | created_at DESC |
| 結合 | JOIN の条件になる列か | orders.user_id = users.id |
頻繁に検索条件へ出る列は、インデックス候補になります。ただし、すべての列にインデックスを張ればよいわけではありません。
インデックスにはコストがある
インデックスは読み取りを速くしますが、書き込みにはコストがかかります。
データを追加・更新・削除すると、テーブル本体だけでなくインデックスも更新しなければならないからです。
| 操作 | インデックスの影響 |
|---|---|
| SELECT | 条件が合えば速くなる |
| INSERT | インデックスにも追加が必要 |
| UPDATE | 対象列が変わるとインデックス更新が必要 |
| DELETE | インデックスからも削除が必要 |
読み取りが多い検索画面では有効でも、大量INSERTするログテーブルではインデックスが多すぎると書き込み性能を落とします。
実行計画で確かめる
インデックスを追加したら、実際に使われているかを確認します。代表的な方法が EXPLAIN です。
EXPLAIN
SELECT *
FROM orders
WHERE user_id = 123
ORDER BY created_at DESC;
PostgreSQLでは、実際の実行時間まで見たい場合に EXPLAIN ANALYZE を使います。
EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE user_id = 123
ORDER BY created_at DESC;
見るポイントは次の通りです。
Seq Scan: テーブル全体を読んでいる可能性があるIndex Scan: インデックスを使っているRows: 想定より多くの行を読んでいないかSort: 並び替えで大きなコストが出ていないか
初心者のうちは、実行計画のすべてを読めなくても構いません。まず「全件読んでいないか」「想定より多く読んでいないか」を見るだけで、調査の精度が上がります。
複合インデックスの考え方
複数の列を組み合わせたインデックスを複合インデックスと呼びます。
CREATE INDEX idx_orders_user_status_created
ON orders(user_id, status, created_at);
このインデックスは、次のようなクエリに効きやすくなります。
SELECT *
FROM orders
WHERE user_id = 123
AND status = 'paid'
ORDER BY created_at DESC;
ただし、複合インデックスは列の順序が重要です。一般的には、よく等価条件で使う列を前に置き、範囲条件や並び替えに使う列を後ろに置くことが多いです。
user_id = 123 -- 等価条件
status = 'paid' -- 等価条件
created_at >= ... -- 範囲条件・並び替え
中級者向け:インデックスが効かない典型例
インデックスを張っていても、クエリの書き方によっては効きにくい場合があります。
列に関数をかけている
-- created_at のインデックスが効きにくい
SELECT *
FROM orders
WHERE DATE(created_at) = '2026-04-29';
列を関数で加工すると、インデックスの順序をそのまま使えないことがあります。範囲条件に書き換えると扱いやすくなります。
SELECT *
FROM orders
WHERE created_at >= '2026-04-29 00:00:00'
AND created_at < '2026-04-30 00:00:00';
前方一致ではないLIKE
-- 通常のB-treeインデックスでは効きにくい
SELECT *
FROM products
WHERE name LIKE '%イヤホン%';
部分一致検索を多用するなら、全文検索エンジンやDB固有の全文検索インデックスを検討します。
絞り込み率が低い
gender や is_deleted のように値の種類が少ない列は、単独インデックスの効果が薄いことがあります。対象行が多すぎると、インデックスをたどるよりテーブルを順番に読む方が速い場合があるためです。
設計時のチェックリスト
インデックス設計では、次の順に確認します。
□ 画面・APIでよく使う検索条件は何か
□ JOIN条件になる外部キーにインデックスがあるか
□ ORDER BY / LIMIT と相性の良いインデックスになっているか
□ 書き込みが多いテーブルにインデックスを増やしすぎていないか
□ EXPLAINで実際に使われているか
□ データ件数が増えた後も同じ設計で耐えられるか
まとめ
- インデックスは検索を速くするが、書き込みコストも増える
WHERE,JOIN,ORDER BYに使う列が候補になる- 追加後は
EXPLAINで実際に使われているか確認する - 複合インデックスは列の順序が重要
- データ件数が少ないうちに速く見えても、本番データ量で再確認する
SQLの基本構文はSQL 基礎 — SELECT・JOIN・集計、整合性を守る考え方はトランザクションとACIDの理解で学べます。