【入門編】 式インデックス (Expression Indexes) – PostgreSQL

こんにちは!データベースの世界へようこそ。

普段、私たちがデータベースをいじっていると、「あれ?なんか動きが重いな」と感じる瞬間ってありますよね。特にデータが増えてくると、検索ボタンを押してから結果が出るまでの「あの待ち時間」がもどかしくなるものです。

そんな時、たいていのエンジニアは「インデックス(索引)」という秘密兵器を使います。本の後ろにある索引と同じで、これがあれば目的のデータがどこにあるか一瞬で見つけられるようになりますよね。

でも、「普通にインデックスを貼ったのに、なぜか速くならない!」という壁にぶつかったことはありませんか?

実はそれ、PostgreSQLの「式インデックス(Expression Indexes)」を使うと、魔法のように解決できるかもしれません。今日は、このちょっと賢いインデックスについて、一緒に紐解いていきましょう。

—

そもそも、なぜ検索が遅くなるの?

まず、身近な例で考えてみましょう。

あなたは図書館の司書さんだと想像してください。本棚にはたくさんの本が並んでいます。タイトル順に並んでいるので、タイトルで探すならすぐに見つかりますよね。

でも、もし誰かが「『タイトルを全部小文字にして、さらに空白を除いたもの』で検索して!」と言ってきたらどうでしょう?
元のタイトル順に並んでいる本棚を、一冊ずつ手に取って、その場で文字を変換してチェックして……なんてやっていたら、日が暮れてしまいますよね。

データベースの世界でも同じことが起きています。
クエリ(検索命令)の中で、データに対して「関数」を使ったり「計算」をしたりすると、データベースはインデックスを無視して、全データを一つずつ加工しながら確認し始めるんです。これじゃあ、遅くなるのも無理はありません。

「式インデックス」は、あらかじめ答えを作っておくこと

ここで登場するのが「式インデックス」です。
さっきの図書館の例で言うと、「検索されやすいように、最初から『小文字にして空白を除いた状態』のラベルを本の背表紙に貼っておく」という作戦です。

これなら、誰かが「小文字で検索したい!」と言った瞬間、そのラベルを見れば一発で本が見つかりますよね。

PostgreSQLでは、インデックスを作る時に、ただの列名ではなく「計算式」そのものを指定することができるんです。

— 例えば、メールアドレスのドメイン部分だけで検索したいとき
CREATE INDEX idx_email_domain ON users (split_part(email, ‘@’, 2));

こうしておけば、`split_part(email, ‘@’, 2)` という関数を毎回実行しなくても、データベースはインデックスの中に計算済みの結果を持っているので、爆速で検索を終えてくれます。

—

こんな場面で使ってみて!

実際に現場でよく使う「おすすめのシチュエーション」をいくつか挙げておきますね。

  • 大文字・小文字を無視した検索

`lower(name)` でインデックスを作れば、`WHERE lower(name) = ‘taro’` という検索が劇的に速くなります。

  • JSONデータの中身で検索

最近はJSONを使うことも多いですよね。`data->>’status’` のような構造にもインデックスが貼れます。

  • 複雑な計算結果で絞り込みたい時

「注文の合計金額が1万円以上」というような計算をよく行うなら、その計算式をインデックスにしてしまうのもアリです。

注意点:何でもかんでも作ればいいわけじゃない

ここまで聞くと「じゃあ全部インデックスにしちゃおう!」と思うかもしれませんが、ちょっと待ってくださいね。

インデックスは、あくまで「本棚の索引」です。索引を増やしすぎると、本を新しく追加したり書き換えたりするたびに、その膨大な索引を全部更新しなきゃいけなくなります。つまり、データの保存や更新が重くなってしまうんです。

「本当によく使う検索条件は何か?」をじっくり見極めて、ピンポイントで適用するのが、プロの腕の見せ所です。

—

最後に

式インデックスは、データベースが本来持っている「柔軟性」と「速さ」を両立させるための、すごく心強い武器です。

「いつも同じ関数を通したデータで検索しているな」と思ったら、ぜひ一度試してみてください。「あれ、こんなに速いの?」と驚くはずですよ。

データベースのチューニングは、料理の隠し味のようなものです。少しの工夫で、システム全体が驚くほど心地よく動くようになる。そんな感覚を、ぜひ楽しんでみてくださいね!

それでは、また次回の記事でお会いしましょう!

コメント

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