【実務・中級編】 マージ結合 – PostgreSQL

やあ。今日もデータベースと格闘してる?

SQLを書いていて、「なぜこのクエリだけやたら遅いんだろう?」と悩むこと、あるよね。実行計画を見てみたら、おなじみの `Hash Join` ではなく `Merge Join` が選ばれている……そんな場面に出くわしたとき、君はどう感じているかな?

「あ、マージ結合か。これって何がいいんだっけ?」と一瞬でも頭をよぎるなら、この記事は君のためにある。今日は、PostgreSQLの職人として、マージ結合の「本質」と「実務での付き合い方」を伝授するよ。

—

1. マージ結合(Merge Join)って結局なんなのか?

教科書的な定義を言えば、「ソート済みの二つの入力を順次読み込みながら結合する方式」だよね。でも、現場ではこうイメージしてほしい。

「二つの長い巻物(ソート済みリスト)を、両手で同時に広げながら、同じ名前を見つけたらすかさずメモする」

これがマージ結合の動きだ。

Nested Loop(入れ子ループ)が「片方のリストを片手に、もう片方を最初から最後まで何度もなぞる」という地獄の作業だとしたら、マージ結合は、お互いがきれいに並んでいるおかげで、一度通り過ぎたら二度と戻らなくていい。だから、データ量が膨大になっても、計算コストが爆発しにくいんだ。

2. なぜPostgreSQLはマージ結合を選ぶのか

PostgreSQLのオプティマイザが「Hash JoinじゃなくてMerge Joinで行こう」と決断するとき、そこには明確な理由があるんだ。

  • ソート済みであること(または、ソートコストが安いこと)
  • すでにインデックスが効いているカラム同士の結合なら、PostgreSQLは追加のソートコストを払わずにマージを開始できる。これが最強のパターンだね。
  • 等価結合(`=`)であること
  • 不等号(`<` や `>`)を含んだ結合条件でも使えるのが、ハッシュ結合に対する大きなアドバンテージだ。これ、意外と忘れられがちだけど重要だよ。
  • メモリが限られているとき
  • ハッシュ結合は大きなハッシュテーブルをメモリ上に作る必要がある。メモリがカツカツの環境だと、ディスクに溢れて(Spill to disk)パフォーマンスがガタ落ちする。マージ結合はストリーミング処理に近いから、メモリ消費が安定しているんだ。

3. 実務で遭遇する「マージ結合の罠」

現場でよくある失敗ケースを一つ紹介しよう。

SELECT
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.created_at > ‘2023-01-01’;

もし `orders` テーブルの `customer_id` にインデックスがなかったらどうなる?
PostgreSQLは「よし、マージ結合のためにわざわざソートしよう」と考える。この「ソート」のコストが巨大だと、ハッシュ結合を選んだほうが早かった……なんてことはザラにある。

先輩からのアドバイス:
`EXPLAIN ANALYZE` を見たとき、`Sort` ノードが実行時間の大部分を占めていないか確認してほしい。もしそうなら、そのカラムにインデックスを貼るか、あるいは結合条件を見直す必要がある。

4. マージ結合を味方につけるためのコード戦略

マージ結合を「狙って」発動させるには、インデックス戦略が鍵になる。

例えば、マスタテーブルとトランザクションテーブルを結合する際、マスタ側でインデックスを貼っておくのは基本だよね。

— customer_id にインデックスがあれば、マージ結合は極めて高速に動く
CREATE INDEX idx_orders_customer_id ON orders(customer_id);

こうしておけば、オプティマイザは「あ、これソート済みとして扱えるな」と判断して、スマートにマージ結合を選択してくれる。実行計画で `Merge Join` を見つけたら、「よし、インデックスがちゃんと活きてるな」とガッツポーズしていい。

最後に:データベースと会話しよう

技術スタックが進歩しても、マージ結合のような「基礎的なアルゴリズム」の重要性は変わらない。結局、データベースエンジンが何を考えているのかを想像できるかどうかが、エンジニアとしての腕の見せ所なんだ。

次に `EXPLAIN` を叩くときは、ただ数字を見るだけじゃなく、「今、PostgreSQLは二つの巻物を同時に広げているんだな」と想像してみてほしい。そうすれば、自ずとどんなインデックスが必要か、どんなクエリを書くべきかが見えてくるはずだよ。

何か詰まったら、またいつでも聞いてくれ。一緒に最高にクールなクエリを追求しようぜ。

コメント

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