SQL 複合インデックス：使うべきタイミングは？

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/6ab80342-efc5-4413-8cc4-009c93dd39b7.png)](https://leapcell.io/?lc_t=q_sqlcompoindex)

## 複合インデックスを検討すべきタイミングは？

複合インデックス（結合インデックス）とは、複数のカラムに対して作成されるインデックスのことです。通常、以下のようなケースで効果的です。

### 複数条件（複数カラムの WHERE フィルタ）を含むクエリ

クエリが複数の条件を含む場合、単一カラムのインデックスでは効果が薄いことがありますが、複合インデックスを使うことでクエリの高速化が可能です。

```sql
SELECT * FROM orders WHERE user_id = 1001 AND status = 'shipped';
```

この場合、`(user_id, status)` の複合インデックスを作成すれば、クエリはインデックスを有効に利用できます。

### インデックスのカラムがクエリで一緒に使われることが多い場合

`col1` と `col2` が WHERE フィルタ、ソート、またはグループ化に頻繁に一緒に使われる場合、`(col1, col2)` の複合インデックスを作成することを検討すべきです。

### クエリでカバリングインデックスを必要とする場合

`SELECT` クエリで使用されるカラムがすべて複合インデックスに含まれている場合、インデックスのデータだけで結果を取得でき、テーブルへのアクセスを避けてパフォーマンスを向上させることができます（カバリングインデックス）。

```sql
SELECT user_id, status FROM orders WHERE user_id = 1001;
```

`(user_id, status)` インデックスが存在すれば、インデックスから直接データを取得でき、テーブルデータにアクセスする必要はありません。

### インデックスのカラム順がクエリの習慣に合っている場合

MySQL のインデックスは「最左前方一致の原則（leftmost prefix）」に従っており、複合インデックス内のカラムの順序が、クエリでインデックスを利用できるかどうかに影響します。

## 条件が一つだけの場合でも複合インデックスは必要か？

必ずしもそうとは限りません。以下のような要因によって判断されます。

### 対象カラムが高選択性かどうか

クエリに使用する単一カラムが**高選択性**（識別性が高い、例えば `user_id` のように一意に近い値が多い）の場合、単一インデックスだけで十分なことが多いです。  
一方、`status` のように値の種類が少ない低選択性のカラムでは、複合インデックスの方が有効です。

### 将来的に条件が追加される可能性があるか

現時点では `WHERE col1 = ?` のように単一条件であっても、将来 `col2` も条件に加える可能性があるなら、あらかじめ `(col1, col2)` の複合インデックスを作成しておく方が良いでしょう。

### ORDER BY や GROUP BY の要件があるかどうか

`ORDER BY col1, col2` や `GROUP BY col1, col2` のようなクエリがよく使われる場合、複合インデックスがソートやグループ化の最適化に役立ちます。

## 例による分析

### user_id のみを検索する場合

```sql
SELECT * FROM orders WHERE user_id = 1001;
```

`user_id` の選択性が高ければ、単一インデックス `(user_id)` で十分です。  
しかし、`status` も頻繁にフィルタ条件に使われ、`WHERE user_id AND status` のようなクエリが多い場合は、`(user_id, status)` の複合インデックスを検討すべきです。

### user_id と status の両方を検索する場合

```sql
SELECT * FROM orders WHERE user_id = 1001 AND status = 'shipped';
```

`(user_id, status)` の複合インデックスは、`(user_id)` 単独インデックスよりも効果的であり、`status` に対する追加フィルタ処理を避けられます。

### status のみを検索する場合

```sql
SELECT * FROM orders WHERE status = 'shipped';
```

もし `(user_id, status)` の複合インデックスが存在していても、`status` は最左前方ではないため、インデックスは完全には活用されず、`(status)` の単一インデックスが必要になる場合があります。

## 結論

- 条件が 1 つのクエリには単一インデックスで足りる場合が多いが、将来的に条件が増える可能性があるなら複合インデックスを検討すべき。
- クエリ条件が複数カラムにまたがる場合は、複合インデックスの方が複数の単一インデックスよりも効率的。
- インデックス設計では、「最左前方一致の原則」「クエリのパターン」「カラムの選択性」「カバリングインデックス」などを総合的に考慮する必要がある。

---

### [私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。](https://leapcell.io/?lc_t=q_sqlcompoindex)

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/2d28da57-969b-481e-9f3f-e780fe6bc305.png)](https://leapcell.io/?lc_t=q_sqlcompoindex)

[Leapcell](https://leapcell.io/?lc_t=q_sqlcompoindex)は、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです：

**複数言語サポート**

- Node.js、Python、Go、Rustで開発できます。

**無制限のプロジェクトデプロイ**

- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

**比類のないコスト効率**

- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。

**洗練された開発者体験**

- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

**簡単なスケーラビリティと高パフォーマンス**

- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。

[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

[![Try Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/dae0b0ad-5990-4184-8d05-7455b98e535a.png)](https://leapcell.io/?lc_t=q_sqlcompoindex)

Xでフォローする：[@LeapcellHQ](https://x.com/LeapcellHQ)

---

[ブログでこの記事を読む](https://leapcell.io/blog/when-to-use-composite-indexes-in-sql)