SQLite | ゼロからはじめるSQL、30日で習得するSQLite:データ操作・設計 - Day19 インデックス

SQL SQLite
スポンサーリンク

Day19 前半

「インデックス=“本の索引”をテーブルに付けて、検索を一気に速くする」

ここまでで、SELECT・JOIN・WHERE・GROUP BY…
いろいろな「検索の技」を覚えてきました。

でも、データ量が増えてくると、
「正しい SQL なのに、遅い」
という問題が必ず出てきます。

今日のテーマ インデックス(INDEX) は、
その「遅さ」を根本から改善するための仕組みです。

一言でいうと、

「よく検索に使う列に、“本の索引”のようなデータ構造を追加しておく」

これがインデックスです。

SQLite では CREATE INDEX で作りますが、
前半ではまず「概念」と「何が嬉しいのか」に集中します。


インデックスの直感的なイメージ

「全ページをめくるか、索引から一気に飛ぶか」

本を読むときのことを思い出してください。

あるキーワード(例えば「インデックス」)がどこに書いてあるか知りたいとき、
最初のページから全部めくって探すのは大変です。

そこで役立つのが「索引」です。

巻末の索引には、

インデックス …… 120ページ, 245ページ
SQL …… 10ページ, 50ページ, 200ページ

のように、「キーワード → ページ番号」の対応表が載っています。

この索引があるおかげで、

「インデックス」と書かれているページを一瞬で見つけられる
→ 本文を全部読む必要がない

という状態になります。

データベースのインデックスも、まさにこれと同じです。

テーブル全体を最初から最後までなめる(全件走査)か、
インデックスを使って「該当しそうな行」に一気にジャンプするか。

インデックスは、後者を可能にする「索引テーブル」だと思ってください。


インデックスがないときの検索

「毎回、全行をチェックする世界」

次のような users テーブルを考えます。

id | name       | email
---+------------+-------------------
 1 | 山田太郎   | taro@example.com
 2 | 佐藤花子   | hanako@example.com
 3 | 鈴木一郎   | ichiro@example.com
...
(この先、10万行続くと想像してください)

ここで、次のクエリを実行するとします。

SELECT *
FROM users
WHERE email = 'hanako@example.com';
SQL

インデックスがない場合、データベースは基本的にこう動きます。

1行目を見る → email が一致するか? → 違う
2行目を見る → 一致するか? → 違う
3行目を見る → 一致するか? → 違う

10万行目まで、ひたすらチェック

つまり、
「全行を順番にチェックして、一致する行を探す」
という動きになります。

データが少ないうちは問題ありませんが、
10万行、100万行と増えていくと、
この「全部見る」がどんどん重くなっていきます。


インデックスがあるときの検索

「email → 行の場所」を覚えておくことで、一気に飛べる

同じ users テーブルに対して、
email 列にインデックスを作ったとします。

(書き方は後半でやりますが、イメージだけ)

CREATE INDEX idx_users_email
ON users (email);
SQL

これを作ると、データベース内部に

email をキーにして、どの行にあるかを素早く探せる“索引構造”

が追加されます。

すると、さきほどのクエリ

SELECT *
FROM users
WHERE email = 'hanako@example.com';
SQL

を実行したとき、データベースは

索引(インデックス)を見て、「hanako@example.com がある行」を一瞬で特定
→ その行だけを取りに行く

という動きに変わります。

本でいうと、

索引ページを開く
「インデックス」の行を探す
書いてあるページ番号に一気に飛ぶ

というのと同じです。

結果として、
「データ量が増えても、検索速度があまり落ちない」
という状態を作れます。


どの列にインデックスを張るべきか

「よく WHERE や JOIN に使う列が候補」

インデックスは「何でもかんでも張ればいい」ものではありません。
(張りすぎると逆に遅くなる話は後半でやります)

基本的な考え方はシンプルで、

「よく検索条件に使う列」「よく JOIN のキーに使う列」にインデックスを張る

です。

例えば、次のようなクエリをよく書くなら、

SELECT * FROM users
WHERE email = ?;
SQL

email にインデックスを張る価値が高いです。

JOIN でよく使う user_id なども同様です。

SELECT *
FROM orders o
INNER JOIN users u
  ON o.user_id = u.id;
SQL

この場合、users.idorders.user_id にインデックスがあると、
JOIN の処理が速くなることが多いです。

逆に、

ほとんど検索に使わない列
値のバリエーションがほとんどない列(例:フラグ 0/1 だけ)

にインデックスを張っても、あまり効果がありません。


PRIMARY KEY とインデックスの関係

「主キーには、すでに“特別なインデックス”が付いている」

ここで一つ大事なポイントがあります。

PRIMARY KEY を付けた列には、データベースが自動的にインデックスを作る

ということです。

例えば、こう定義したとします。

CREATE TABLE users (
  id    INTEGER PRIMARY KEY,
  name  TEXT NOT NULL,
  email TEXT
);
SQL

このとき、id には自動的にインデックスが付きます。

なので、

SELECT * FROM users
WHERE id = 123;
SQL

のようなクエリは、
すでに「インデックスを使った高速検索」になっています。

わざわざ CREATE INDEXid にインデックスを張る必要はありません。

インデックスを意識すべきなのは、

PRIMARY KEY 以外の列(email や user_id など)で
よく検索・JOIN をしているところ

です。


セキュリティの視点から見るインデックス

「速くなることは、攻撃者にとっても“便利”になり得る」

インデックスは性能チューニングの話ですが、
セキュリティの視点からも少しだけ触れておきます。

インデックスを張ると、

特定の条件での検索が速くなる
→ 正常なユーザーにとって便利

なのと同時に、

攻撃者が「特定のユーザーを探す」「特定のパターンをなめる」
といった操作も速くなる可能性があります。

だからといって「インデックスを張るな」という話ではありません。

大事なのは、

インデックスを張るかどうかよりも、
「そもそもそのテーブル・その列に誰がアクセスできるのか」
をきちんと制御することです。

インデックスはあくまで「速く探すための道具」であって、
アクセス権限の代わりにはなりません。


小さな練習イメージ

頭の中で、次のような状況を想像してみてください。

users テーブルで、email でよく検索している。
orders テーブルで、user_id でよく JOIN している。
ログイン履歴 login_logs で、user_id と created_at でよく絞り込んでいる。

「どの列にインデックスを張ると効果がありそうか?」
という視点で考えてみると、
インデックスの“当たりどころ”が少し見えてくるはずです。


Day19 前半のまとめ

インデックスは「よく検索に使う列に対して、本の索引のような構造を追加して、検索を速くする仕組み」。
インデックスがないと、WHERE の条件に合う行を探すために、テーブル全行を順番にチェックする必要がある。
インデックスがあると、「キー → 行の場所」の対応表を使って、一気に該当行にジャンプできる。
PRIMARY KEY には自動的にインデックスが付くので、id での検索は最初から高速になっている。
インデックスを張るべきなのは、「よく WHERE や JOIN に使う列」であり、張りすぎや不要な列へのインデックスは逆効果になり得る(この話は後半で詳しく)。

後半では、

実際の CREATE INDEX の書き方
複数列インデックス(複合インデックス)のイメージ
インデックスのデメリット(更新が重くなる・ディスクを食う)
「本当に必要なインデックスだけを選ぶ」考え方

まで踏み込んで、インデックスを“ちゃんと使いこなす”ところまで持っていきます。

タイトルとURLをコピーしました