結合アルゴリズムの選択基準
Oracle CBO は以下の要素を考慮して結合方式を選択します。
- 結合する行数: 少量 × 少量 → NESTED LOOPS、大量 × 大量 → HASH JOIN
- インデックスの存在: 結合キーにインデックスがあれば NESTED LOOPS が有利になりやすい
- ソート済みデータの有無: 結合キー順にソート済みなら SORT MERGE JOIN のソートコストが減る
- 利用可能なメモリ(PGA): HASH JOIN / SORT MERGE JOIN はメモリが不足すると TEMP SPILL が発生
- 結合条件の種類: 等値(=)以外の不等値条件では HASH JOIN が使えず NESTED LOOPS か SORT MERGE JOIN になる
NESTED LOOPS(ネステッドループ結合)
何を表すか: 外側ループ(Outer / 駆動表)の各行について、内側ループ(Inner / 被駆動表)をスキャンして一致する行を結合する方式。
Plan Table 上での読み方
NESTED LOOPS ノードの直下に 2 つの子ノードが並びます。上が外側ループ(駆動表)、下が内側ループ(被駆動表)です。
- 正常パターン: 駆動表が少数行(数十〜数百)、被駆動表が結合キーに対してインデックスアクセスできる場合
- 問題パターン: 駆動表が大量行 → 内側ループが何万回も実行される(OLAP 的クエリでの NESTED LOOPS は非効率)
- 閾値・注意点: ALLSTATS の Starts 列で内側ループの実行回数を確認。外側 Rows × Starts が内側ループの実際の起動数
NESTED LOOPS の種類と特徴
| Operation | 意味 | 注意点 |
|---|---|---|
| NESTED LOOPS | 通常のネステッドループ結合 | 内側が INDEX RANGE SCAN + TABLE ACCESS BY ROWID の組み合わせが典型 |
| NESTED LOOPS OUTER | OUTER JOIN(LEFT/RIGHT)のネステッドループ実装 | 一致しない外側行に NULL 補完行を追加 |
| NESTED LOOPS SEMI | EXISTS / IN サブクエリを SEMI JOIN で実装 | 最初の一致が見つかれば内側ループ終了(効率的) |
| NESTED LOOPS ANTI | NOT EXISTS / NOT IN の ANTI JOIN 実装 | 一致した場合に行を除外。NULL の扱いに注意 |
HASH JOIN(ハッシュ結合)
何を表すか: Build 側(上のノード)の行セットをメモリ上のハッシュテーブルに展開し、Probe 側(下のノード)を流し込んで一致する行を結合する方式。大規模テーブル同士の等値結合に最適。
Plan Table 上での読み方
HASH JOIN ノードの上の子が Build 側(小さいほうの表を選ぶのが理想)、下の子が Probe 側です。
- 正常パターン: 大規模テーブル同士の等値結合。Build 側に小テーブルが配置されている
- 問題パターン①: Build 側が大きすぎてメモリに収まらず TEMP SPILL(
HASH JOIN BUFFERED)が発生 → PGA 増量かバッチ分割を検討 - 問題パターン②: 不等値結合(>=、<=)では HASH JOIN が使えない(オプティマイザが自動的に別方式を選ぶ)
- 閾値・注意点: Build 側の Bytes が PGA_AGGREGATE_TARGET の約 5% を超えると SPILL が疑われる
HASH JOIN の種類と特徴
| Operation | 意味 | 注意点 |
|---|---|---|
| HASH JOIN | 通常の内部結合版ハッシュ結合 | 等値条件必須。最も一般的 |
| HASH JOIN OUTER | OUTER JOIN のハッシュ結合実装 | Build 側が外部表(結果に残す側) |
| HASH JOIN SEMI | EXISTS / IN のハッシュ SEMI JOIN | 重複除去が内包される |
| HASH JOIN ANTI | NOT EXISTS / NOT IN のハッシュ ANTI JOIN | NULL を含む NOT IN は全行 NULL のケースで誤動作に注意 |
| HASH JOIN RIGHT OUTER | 右外部結合のハッシュ実装 | Build/Probe の順が逆転 |
SORT MERGE JOIN(ソートマージ結合)
何を表すか: 両テーブルを結合キーでソートしてから、マージして結合する方式。ソート済みの結果セットに対しては効率的で、ORDER BY が後続する場合にはソートの再利用が可能。
- 正常パターン: データが既にソート済み(パーティションキー順など)・ORDER BY と結合キーが一致・不等値結合(>、< 等)
- 問題パターン: 大規模テーブルの等値結合で HASH JOIN の方が PGA 消費が少ないのに SORT MERGE JOIN が選ばれた場合(統計情報の誤りが原因なことが多い)
- 閾値・注意点: 両テーブルの Bytes が大きいと 2 回のソートで TEMP SPILL が発生する可能性あり
MERGE JOIN CARTESIAN(直積)
何を表すか: 結合条件なし(またはすべてのクロス結合)で 2 つの表の全行を掛け合わせた直積を生成する結合。意図的な CROSS JOIN でない限り 計画に出現したら要警戒です。
- 読み方: Rows = 左の行数 × 右の行数(爆発的増加)
- 問題パターン: 結合条件の書き忘れ(テーブル間に ON 句がない)・FROM 句に結合条件のない表が含まれている・サブクエリのスカラー化でカーティシャンが発生
- 閾値・注意点: 数百万行の直積はメモリ・TEMP を瞬時に枯渇させる。計画に見えたらまず SQL の結合条件を確認する
- 正常パターン: 意図的な CROSS JOIN(すべての組み合わせが必要なビジネスロジック)
結合方式の比較表
| 比較項目 | NESTED LOOPS | HASH JOIN | SORT MERGE JOIN |
|---|---|---|---|
| 等値結合 | ◯ | ◯ | ◯ |
| 不等値結合 | ◯ | ✕ | ◯ |
| 大規模 × 大規模 | △(遅い) | ◎ | ◯ |
| 小規模 × 大規模(インデックスあり) | ◎ | ◯ | △ |
| ソート済みデータへの効率 | 影響なし | 影響なし | ◎(ソートコスト削減) |
| メモリ消費 | 少ない | 多い(Build 側分) | 多い(ソート分×2) |
| TEMP SPILL リスク | 低い | あり | あり |
| 強制ヒント | USE_NL(table) | USE_HASH(table) | USE_MERGE(table) |