SQLite | ゼロからはじめるSQL、30日で習得するSQLite:実践 - Day28 パフォーマンス基礎

SQL SQLite
スポンサーリンク

Day28 後半のゴール

「インデックスが“効く/効かない”を、自分で見抜けるようになる」

前半で、インデックスが「検索専用の索引」だというイメージはつかめました。
後半ではもう一歩踏み込んで、

インデックスあり/なしで、実際にどう挙動が変わるかをイメージする
インデックスが効かない書き方・効きにくいパターンを知る
複合インデックスの“向き”の考え方をつかむ

ところまで行きます。
ここを越えると、「なんとなくインデックス」から卒業できます。


インデックスあり/なしの違いを“頭の中で実験”する

「小さいテーブルでは差が出ない、だからこそイメージで持っておく」

まず、シンプルなテーブルを想像します。

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

ここで、10件だけデータが入っているとします。
この状態で、

SELECT *
FROM customers
WHERE email = 'taro@example.com';
SQL

を実行しても、インデックスの有無で体感差はほぼありません。
10件ならフルスキャンしても一瞬だからです。

でも、これが10万件、100万件になってくると話が変わります。

インデックスなし
先頭から100万行、1行ずつ「email が一致するか」を見る

インデックスあり
索引構造をたどって、ほぼ一瞬で該当行の位置にたどり着く

SQLite では EXPLAIN QUERY PLAN で実際の実行計画を見られますが、
ここでは「行数が増えたときの差」を頭の中でイメージできれば十分です。

「少ないデータだと差が見えないけど、
現場のテーブルは平気で何十万行になる」

このギャップを意識しておくと、
インデックスの重要性が現実味を帯びてきます。


インデックスが“効かない書き方”の典型例

「カラムに関数をかけると、索引カードが使えなくなる」

インデックスは、「カラムの値そのもの」をキーにして作られます。
なので、WHERE でそのカラムに関数をかけてしまうと、
インデックスが使われないことがよくあります。

例えば、ordered_at にインデックスを張っているとします。

CREATE INDEX idx_orders_ordered_at
  ON orders(ordered_at);
SQL

このとき、

SELECT *
FROM orders
WHERE ordered_at >= '2025-05-01 00:00:00';
SQL

はインデックスが効きやすい書き方です。
ordered_at の値そのものを使って比較しているからです。

一方で、こう書いてしまうとどうでしょう。

SELECT *
FROM orders
WHERE date(ordered_at) = '2025-05-01';
SQL

date(ordered_at) という「関数を通した結果」と
文字列 '2025-05-01' を比較しています。

インデックスは「生の ordered_at の値」に対して作られているので、
date(ordered_at) の結果に対しては使えません。

イメージとしては、

索引カードには「2025-05-01 10:00:00」のような“生の値”が並んでいるのに、
「この値を日付だけに切り詰めた結果」で探そうとしている

という状態です。

日付で絞りたいなら、本当はこう書く方がインデックスフレンドリーです。

SELECT *
FROM orders
WHERE ordered_at >= '2025-05-01 00:00:00'
  AND ordered_at <  '2025-05-02 00:00:00';
SQL

「関数をカラム側にかけるとインデックスが効きにくい」
これはかなり重要なパターンなので、強めに覚えておいてください。


前方一致と後方一致でのインデックスの効き方

「LIKE 'abc%' は効きやすいが、LIKE '%abc' は厳しい」

文字列検索でも、インデックスの効き方に差があります。

例えば、email にインデックスがあるとします。

CREATE INDEX idx_customers_email
  ON customers(email);
SQL

このとき、

SELECT *
FROM customers
WHERE email LIKE 'taro%';
SQL

は、インデックスが効きやすい書き方です。
先頭から「taro」で始まる範囲を、索引で一気に絞り込めるからです。

一方で、

SELECT *
FROM customers
WHERE email LIKE '%@example.com';
SQL

のように、先頭がワイルドカードだと、
多くのデータベースでインデックスが効きにくくなります。

「どこに ‘@example.com’ が出てくるか分からない」ので、
結局かなり広い範囲をなめる必要が出てくるからです。

SQLite も基本的には同じで、

前方一致('abc%')はインデックスと相性が良い
後方一致・部分一致('%abc', '%abc%')は効きにくい

という感覚を持っておくと、
検索条件の設計をするときに役立ちます。


複合インデックスの“向き”を理解する

「左から順に使われる。真ん中からはいきなり使えない」

複数カラムにまたがるインデックス(複合インデックス)もあります。

例えば、注文テーブルにこう張るとします。

CREATE INDEX idx_orders_customer_date
  ON orders(customer_id, ordered_at);
SQL

これは、

まず customer_id で並べ、その中で ordered_at 順に並べた索引

だと思ってください。

このとき、インデックスが効きやすいのはこういうクエリです。

SELECT *
FROM orders
WHERE customer_id = 1
  AND ordered_at >= '2025-05-01 00:00:00';
SQL

customer_id で範囲を絞り、その中で ordered_at を使ってさらに絞る、
という形でインデックスが使われます。

一方で、こう書いた場合はどうでしょう。

SELECT *
FROM orders
WHERE ordered_at >= '2025-05-01 00:00:00';
SQL

この場合、「左側の customer_id を使っていない」ので、
(customer_id, ordered_at) の複合インデックスは活かしにくくなります。

複合インデックスは、

左から順に条件に使われているときに真価を発揮する
真ん中や右側だけを使う条件では、あまり役に立たない

という性質があります。

だからこそ、

「どの組み合わせで検索することが多いか」
「そのとき、左から順に条件に使えるか」

を考えて、複合インデックスを設計する必要があります。


インデックスを“効かせる書き方”を意識する

「同じ条件でも、書き方次第で速さが変わる」

ここまでの話をまとめると、
インデックスを効かせるためには、次のような書き方を意識する必要があります。

カラムに関数をかけず、“生の値”で比較する
日付なら「>= 開始日時 AND < 終了日時」のように範囲で書く

文字列の前方一致を活かす
LIKE 'abc%' のように、先頭からの一致を使う

複合インデックスの左側から順に条件を書く
(customer_id, ordered_at) なら、customer_id を必ず条件に含める

同じ「欲しい結果」でも、
書き方次第でインデックスが効いたり効かなかったりします。

これは、
「SQL は見た目が同じでも、中でやっていることが全然違う」
という典型的なポイントです。


インデックスとセキュリティ・運用の“地味だけど大事な関係”

「重いクエリは、システム全体の“攻撃面”にもなりうる」

パフォーマンスの悪いクエリは、
単に「遅い」だけでは終わりません。

例えば、

インデックスが効いていない重い検索を、
誰でも何度でも叩ける画面がある

という状態は、

「簡単にサービスを重くできるポイントが、外に丸見え」

とも言えます。

極端な話、
ログインしていないユーザーでも叩ける API が、
毎回フルスキャンするようなクエリを実行していたら、
それだけで DoS 的な攻撃の入口になります。

だからこそ、

よく叩かれるクエリほど、インデックスを含めてきちんと設計する
重いクエリを叩けるのは、認証済み・権限のあるユーザーに絞る

といった視点が必要になります。

インデックスは「速くするための道具」ですが、
裏を返せば「速くしないと危ない場所」を教えてくれる道具でもあります。


Day28 後半のまとめ

インデックスの効果は小さいテーブルでは体感しづらいが、行数が増えるほど「フルスキャン」と「索引をたどる」の差が爆発的に広がる。
カラムに関数をかけた条件(date(ordered_at) = ... など)や、先頭がワイルドカードの LIKE '%abc' は、インデックスが効きにくい典型パターンで、「生のカラムをそのまま比較する」「前方一致を使う」書き方を意識するとよい。
複合インデックスは「左から順に」使われる性質があり、(customer_id, ordered_at) なら customer_id を条件に含めてこそ真価を発揮する。真ん中や右側だけを使う条件では効果が薄い。
同じ意味の検索でも、書き方次第でインデックスが効いたり効かなかったりするため、「インデックスをどう張るか」と同じくらい「インデックスをどう“活かす書き方”にするか」が重要になる。
インデックスが効いていない重いクエリは、単なる“遅さ”ではなく、サービス全体の安定性や攻撃耐性にも影響するため、「よく叩かれるクエリほどきちんと設計し、必要なインデックスを張る」という意識がセキュリティ面でも大事になる。

ここまで押さえたあなたは、
「インデックスをなんとなく張る人」から、
「効かせ方・効かないパターンまで含めて設計できる人」に一歩踏み出しています。
あとは実際に、自分のクエリで「この書き方、インデックス効いてるかな?」と疑う癖をつけていけば、現場レベルの感覚がどんどん磨かれていきます。

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