メインコンテンツへ移動
SEMentor
データベース 約13分 データベース curriculum

インデックス設計入門

SQLが遅い原因を切り分けるために、インデックスの仕組み、実行計画、設計時の注意点を学ぶ

インデックスは「検索を速くする目次」

データベースのインデックスは、本の索引に似ています。索引があれば、特定のキーワードが載っているページを最初から最後まで探さずに見つけられます。

テーブルでも同じです。インデックスがない場合、データベースは条件に合う行を探すためにテーブル全体を読むことがあります。これを全件スキャンと呼びます。

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固有の全文検索インデックスを検討します。

絞り込み率が低い

genderis_deleted のように値の種類が少ない列は、単独インデックスの効果が薄いことがあります。対象行が多すぎると、インデックスをたどるよりテーブルを順番に読む方が速い場合があるためです。

設計時のチェックリスト

インデックス設計では、次の順に確認します。

□ 画面・APIでよく使う検索条件は何か
□ JOIN条件になる外部キーにインデックスがあるか
□ ORDER BY / LIMIT と相性の良いインデックスになっているか
□ 書き込みが多いテーブルにインデックスを増やしすぎていないか
□ EXPLAINで実際に使われているか
□ データ件数が増えた後も同じ設計で耐えられるか

まとめ

  • インデックスは検索を速くするが、書き込みコストも増える
  • WHERE, JOIN, ORDER BY に使う列が候補になる
  • 追加後は EXPLAIN で実際に使われているか確認する
  • 複合インデックスは列の順序が重要
  • データ件数が少ないうちに速く見えても、本番データ量で再確認する

SQLの基本構文はSQL 基礎 — SELECT・JOIN・集計、整合性を守る考え方はトランザクションとACIDの理解で学べます。

Beginner to Intermediate

インデックス設計入門を実務につなげる学び方

データベースは、言葉を知るだけでなく「どこで使う知識か」まで結びつけると定着します。初学者は全体像をつかみ、中級者は切り分けや設計判断に使える形へ伸ばしていきましょう。

初心者の到達点

  • テーブル、行、列、主キー、外部キーを、業務データの関係として説明する。
  • SELECT文を読むときは、取得対象・条件・並び順・集計の順で意味を確認する。

中級者の観点

  • インデックス、トランザクション、正規化を、性能と整合性のトレードオフとして判断する。
  • データ不整合や遅いクエリを、実行計画・ロック・件数増加と結びつけて調べる。

手を動かす練習

  • 1つの画面に必要なデータを、どのテーブルから取得するかER図として描く。
  • INSERT/UPDATE/DELETEで失敗した場合に、ロールバックすべき単位を考える。

このレッスンは未完了です。