データベースのインデックス(INDEX)とは?メリット・デメリットと作成・確認・削除まで実例で完全解説
「検索が遅い」「JOINが重い」「ORDER BYで時間が溶ける」――その原因、インデックス不足かもしれません。
一方で、インデックスは闇雲に貼ると更新が遅くなる・容量が増えるなどの副作用もあります。
この記事では、現場で役立つように「インデックス(INDEX)とは何か」から「使えるDB」「メリット・デメリット」「具体的なSQL例」「作成・確認・削除方法」まで、実用寄りでまとめます。
インデックス(INDEX)とは何か
インデックスとは、テーブルのデータを素早く見つけるための“検索用の目次”です。
本を探すときに、1ページ目から全部読むのではなく、巻末の索引(index)で目的の単語を引くのと同じ発想です。
DBはインデックスがないと、条件に合う行を探すために全件走査(フルスキャン)しがちです。
インデックスがあると、条件に合う行の位置を効率よく辿れるため、特に件数が多いテーブルほど効きます。
インデックスが効きやすい典型パターン
- WHEREで絞る(例:日付範囲、ID、ステータス)
- JOINの結合キー(例:user_id、order_id)
- ORDER BY / GROUP BY(並び替え・集計)
- UNIQUE制約(重複禁止)
よくある誤解
- インデックスを貼れば何でも速くなる → 更新が遅くなるなどの代償がある
- たくさん貼れば最強 → 使われないインデックスは負債
- LIKE ‘%abc%’ でも速くなる → 多くのDBで前方一致以外は効きにくい(例外あり)
インデックスが使えるDBの種類(代表例)
結論:主要なRDB(リレーショナルDB)はほぼ全てインデックス機能を持っています。
- MySQL(InnoDB)
- PostgreSQL
- Microsoft SQL Server
- Oracle Database
- SQLite
- MariaDB
またNoSQLでも、MongoDBやElasticsearchなどは「インデックス」に近い仕組みを持ちますが、
この記事はまずSQL系(RDB)を中心に解説します。
インデックスの主な種類(ざっくり理解でOK)
- B-tree系:最も一般的。範囲検索(>,<,BETWEEN)や並び替えに強い
- Hash系:等価検索(=)に強い(DBによって扱いが異なる)
- FULLTEXT:全文検索向け(MySQL/PostgreSQLなど)
- Spatial:地理空間(緯度経度など)向け
迷ったらまずは B-tree系の通常インデックス を基準に考えるのが実務的です。
インデックス作成のメリット(効果が出る場面)
1)WHERE検索が速くなる(最重要)
例:日付やステータスで絞る検索が多い場合、インデックスで劇的に変わります。
-- 例:受注テーブル
SELECT *
FROM orders
WHERE customer_id = 123
AND order_date >= '2026-01-01'
AND order_date < '2026-02-01';
このとき、次のような複合インデックス(customer_id + order_date)が有効になりやすいです。
-- MySQL / PostgreSQL(基本形)
CREATE INDEX idx_orders_customer_date
ON orders (customer_id, order_date);
2)JOINが速くなる(結合キーが重要)
JOINは「結合先を探す」動きが入るため、結合キーにインデックスがないと重くなりやすいです。
SELECT o.id, o.order_date, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.order_date >= '2026-01-01';
基本は以下を意識します。
- 主キー(customers.id)は通常インデックスあり
- 外部キー(orders.customer_id)にインデックスがあると強い
CREATE INDEX idx_orders_customer_id
ON orders (customer_id);
3)ORDER BY / GROUP BY が速くなることがある
並び替えや集計は、インデックスの並びを利用できると速くなる場合があります。
SELECT *
FROM orders
WHERE customer_id = 123
ORDER BY order_date DESC
LIMIT 50;
CREATE INDEX idx_orders_customer_date_desc
ON orders (customer_id, order_date);
DBによってはDESC指定をインデックスに持てる/持てない等の違いがありますが、
「絞り込み(customer_id)→並び替え(order_date)」の順に並べた複合インデックスは定番です。
4)UNIQUE制約で重複を防げる(品質が上がる)
-- メールアドレスの重複禁止など
CREATE UNIQUE INDEX ux_users_email
ON users (email);
インデックス作成のデメリット(落とし穴)
1)INSERT/UPDATE/DELETEが遅くなる
インデックスは「目次」なので、データが増減するたびに目次も更新されます。
インデックスが多いほど、更新系(書き込み)が重くなります。
2)ストレージ容量が増える
インデックスは別構造として保持されます。テーブルが巨大になるほど、インデックスも肥大化します。
3)設計を間違えると使われない(=無駄)
- カーディナリティ(値の種類)が少なすぎる列(例:性別、フラグ)に単独インデックス
- 関数をかけたWHERE(例:WHERE DATE(created_at) = ‘…’)でインデックスが効かない
- LIKE ‘%abc%’ のような前方ワイルドカード
- 複合インデックスの列順がクエリと合っていない
4)運用・保守の負債になる
使われないインデックスが増えると、更新性能を下げるだけでなく、チューニング時の判断も難しくなります。
「作った経緯が不明なインデックス」は典型的な技術的負債です。
具体的な記述例(実務でよく使うパターン)
単一インデックス(基本)
CREATE INDEX idx_orders_order_date
ON orders (order_date);
複合インデックス(超重要:列順が命)
複合インデックスは、左から順に効きます(一般に「左端一致」)。
-- (customer_id, order_date) の場合
-- 〇: customer_idで絞る
-- 〇: customer_id + order_dateで絞る
-- △/×: order_dateだけで絞る(DBと状況によるが効きづらいことが多い)
CREATE INDEX idx_orders_customer_date
ON orders (customer_id, order_date);
カバリングインデックス(取得列まで含めて“テーブルを見に行かない”狙い)
DBや状況によりますが、インデックスだけで必要な列が揃うと高速化することがあります。
-- 例:一覧表示で id と order_date だけ返すなら
CREATE INDEX idx_orders_customer_date_id
ON orders (customer_id, order_date, id);
部分(条件付き)インデックス(PostgreSQLなど)
「特定条件だけ頻繁に検索する」なら、対象を絞ったインデックスが効くことがあります。
-- PostgreSQL例:有効ユーザーだけ
CREATE INDEX idx_users_active_email
ON users (email)
WHERE is_active = true;
関数インデックス(DBによる)
「関数をかけるから効かない」を回避するために、DBによっては式/関数のインデックスが作れます。
-- PostgreSQL例:lower(email) で検索する運用なら
CREATE INDEX idx_users_lower_email
ON users (lower(email));
インデックスの作成・確認・削除方法(DB別)
MySQL / MariaDB
作成
CREATE INDEX idx_orders_customer_id
ON orders (customer_id);
CREATE UNIQUE INDEX ux_users_email
ON users (email);
確認
SHOW INDEX FROM orders;
削除
DROP INDEX idx_orders_customer_id ON orders;
PostgreSQL
作成
CREATE INDEX idx_orders_customer_id
ON orders (customer_id);
CREATE UNIQUE INDEX ux_users_email
ON users (email);
確認
-- 一覧
SELECT schemaname, tablename, indexname, indexdef
FROM pg_indexes
WHERE tablename = 'orders';
-- あるテーブルの詳細(psql)
-- \d orders
削除
DROP INDEX idx_orders_customer_id;
SQL Server
作成
CREATE INDEX idx_orders_customer_id
ON dbo.orders (customer_id);
CREATE UNIQUE INDEX ux_users_email
ON dbo.users (email);
確認
SELECT i.name, i.type_desc
FROM sys.indexes i
WHERE i.object_id = OBJECT_ID('dbo.orders');
削除
DROP INDEX idx_orders_customer_id ON dbo.orders;
Oracle
作成
CREATE INDEX idx_orders_customer_id
ON orders (customer_id);
CREATE UNIQUE INDEX ux_users_email
ON users (email);
確認
SELECT index_name, table_name
FROM user_indexes
WHERE table_name = 'ORDERS';
削除
DROP INDEX idx_orders_customer_id;
SQLite
作成
CREATE INDEX idx_orders_customer_id
ON orders (customer_id);
確認
-- インデックス一覧
PRAGMA index_list('orders');
-- 定義確認
SELECT sql
FROM sqlite_master
WHERE type = 'index' AND name = 'idx_orders_customer_id';
削除
DROP INDEX idx_orders_customer_id;
「本当に効いてる?」を確認する:実務のチェック手順
1)まずは実行計画(EXPLAIN)を見る
インデックス設計は“体感”ではなく、実行計画で判断するのが近道です。
-- MySQL / PostgreSQL
EXPLAIN
SELECT *
FROM orders
WHERE customer_id = 123
AND order_date >= '2026-01-01';
ポイントは次のようなイメージです。
- フルスキャン(全件)っぽい動きになっていないか
- 狙ったインデックスが使われているか
- 見積行数が不自然に大きくないか
2)“使われないインデックス”を増やさない
- 似たインデックスの重複(例: (a) と (a,b) の両方)
- 昔の機能の名残
- クエリが変わって役に立っていない
インデックスは「作って終わり」ではなく、使われ方に合わせて見直す資産です。
失敗しないインデックス設計のコツ(実用チェックリスト)
- WHERE / JOIN / ORDER BY を頻出順に棚卸しする
- 大きいテーブルから優先(件数が少ないと差が出にくい)
- 複合インデックスは列順をクエリに合わせる(絞り込み→範囲→並び替えの順が多い)
- 更新が多いテーブルは貼りすぎ注意(更新性能と相談)
- 関数をWHEREにかけない設計(または関数インデックス/生成列を検討)
- 「LIKE ‘%xxx%’」は全文検索や別方式も検討(FULLTEXTなど)
まとめ:インデックスは“速さ”と“重さ”のトレードオフ
インデックス(INDEX)は、DBの検索やJOINを速くする最重要のチューニング要素です。
ただし貼りすぎると、更新が遅くなり容量も増え、保守負債にもなります。
- 遅いクエリはまず EXPLAIN を見る
- 効かせたいWHERE/JOINに合わせて 最小限のインデックス を貼る
- “作って終わり”にせず、運用で見直す
「データベース インデックス」で迷ったら、この記事の手順どおりに
クエリ棚卸し → EXPLAIN → 必要な列にインデックスの順で進めるのが、最短ルートです。
※参考にされる場合は自己責任でお願いします。
PCトラブルは解決しましたか?
もし「会社のPCが全部遅い」「Office 365のエラーが多発する」「ネットワークが不安定」といった、調べても解決しない「会社全体」のお悩みがありましたら、ぜひご相談ください。
「Windows11 高速化」といったお悩み検索で毎月1,200人以上が訪れる、
このサイトの運営者(建設会社IT部長)が、川崎・横浜・東京城南エリアの法人様限定で「無料ITお困りごと診断」を行っています。
