TL

結合順序探索:動的計画法と遺伝的/貪欲法

多数の表を結合するクエリが突然遅くなる理由が腑に落ちます。結合順序の探索空間がなぜ爆発し、動的計画法と貪欲法・遺伝的アルゴリズムがどう枝刈りするかを原理から解説します。

応用クエリ最適化結合順序動的計画法オプティマイザ遺伝的アルゴリズム最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.N 表の結合順序は組合せ爆発する。左深木に限っても N! 通り、ブッシー木まで含めると候補数は超指数的に増え、十数表で全列挙は非現実的になる。
  • 2.System R 流のボトムアップ動的計画法は部分集合ごとに最良プランを1回だけ求めて再利用し、計算量を N! から約 3 の N 乗(部分集合×分割)へ落とす。
  • 3.表数が閾値を超えると DP も破綻するため、貪欲法で逐次的に最小コストの結合を選ぶか、遺伝的アルゴリズムで近似最適解を現実的な時間で得る。

なぜ結合順序が最適化の核心なのか

複数の表を結合するクエリで、結果は結合の順序を変えても同じです(結合の交換律・結合律)。しかし中間結果の行数は順序で桁違いに変わります。先に絞り込める結合を選べば後段が運ぶ行が減り、選び損ねれば巨大な中間結果が膨らみます。クエリオプティマイザにとって、各結合のアルゴリズム選択と並ぶ最大の探索対象がこの結合順序です。本稿では、その候補がなぜ爆発し、どう現実的な時間に収めるかを追います。

探索空間の指数爆発

結合順序は「結合木の形」で表されます。木の形には大きく2系統あります。

木の形特徴候補数(N 表)
左深木(left-deep)右側は常に元の表。逐次的に1表ずつ足すN! 通り(順列)
ブッシー木(bushy)中間結果同士の結合も許す(2(N-1))! / (N-1)! 通りに近い超指数

左深木に限っても、N 表なら順列の総数 N! 通りです。N=10 で約 360 万、N=12 で約 4.8 億に達します。中間結果同士を結合するブッシー木まで許すと候補数はさらに跳ね上がり、結合木の総数はカタラン数を含む超指数的な増加になります。各候補について物理アルゴリズム(Nested Loop / Hash / Merge)の選択も掛かるので、素朴な全列挙は十数表で破綻します。

なぜ左深木が好まれるのか

多くの実装が探索を左深木に限定します。理由は2つあります。第一に候補数が N! に収まり扱いやすいこと。第二に、外側(駆動表)を順に積み上げる左深木は Nested Loop のパイプライン実行と相性が良く、内側を索引で引ける形になりやすいことです。ブッシー木は並列実行で中間結果を同時に作れる利点があり、近年は許す実装も増えています。

System R 流のボトムアップ動的計画法

全列挙を避ける古典的な解が、System R(IBM の初期 RDB)が確立したボトムアップ動的計画法(DP)です。鍵は「同じ表の部分集合に対する最良プランは1つだけ」という観察にあります。例えば {A,B,C} を結合した中間結果の最良プランは、後段でそれをどう使うかと独立に決まります(出力順序を考慮しない単純化では)。ならば部分集合ごとに最良プランを一度だけ求めて記録し、より大きい集合の構築に再利用すればよい——これが最適部分構造の利用です。

# ボトムアップ DP の骨子(左深木の場合)
1. 各単一表 {Ti} の最良アクセス方式(Seq / Index)を求め、表に記録
2. サイズ s = 2 から N まで順に:
     サイズ s の各部分集合 S について:
       S を「サイズ s-1 の部分集合 S' + 残り1表 T」へ分割する全方法を試し、
       (S' の最良プラン) ⋈ T を各結合アルゴリズムで作り、最小コストを S の最良として記録
3. 全表を含む集合 {T1..TN} の最良プランを採用

DP が効くのは、部分集合 {A,B} の最良結合プランを計算するのは一度きりで、{A,B,C} でも {A,B,D} でも同じ結果を参照するからです。素朴な全列挙が同じ部分結合を何度も作り直すのに対し、DP は重複計算を排除します。

計算量はどう下がるか

DP の作業量は「部分集合の個数 × 各集合の分割方法」で決まります。N 表の部分集合は 2 の N 乗個あり、各集合を2分割する組合せまで数えると総量は約 3 の N 乗 のオーダーになります。N=12 なら N!(約 4.8 億)に対し 3 の 12 乗 は約 53 万。桁違いに小さく、これが DP を実用にする本質です。それでも 2 の N 乗 の DP 表をメモリに持つため、N が大きいほどメモリ消費が問題になります。

カーディナリティ推定がコストを決める

DP の各ステップで「最小コスト」を比較するには、各部分プランの推定コストと出力行数(カーディナリティ)が要ります。コストは下から伝播し、{A,B} の推定行数が次の {A,B,C} のコスト計算に入ります。したがって下流の行数推定が外れると、上流まで誤差が増幅し、DP が「最良」と判断したプランが実際には最悪になり得ます。

推定中間行数(A ⋈ B) ≈ rows(A) × rows(B) × 結合選択率
結合選択率 ≈ 1 / max(distinct(A.key), distinct(B.key))   -- 等値結合・独立の仮定

選択率は統計情報(distinct 値数・ヒストグラム)から見積もります。複数結合をまたぐと推定誤差が掛け合わさって増幅するため、表数が多いほど推定はあてにならなくなります。皮肉なことに、探索空間が最も爆発する「多表結合」こそ、コスト見積もりが最も不正確になる領域です。

貪欲法による枝刈り

DP すら重い大規模結合では、**貪欲法(greedy)**が使われます。原理は単純で、毎ステップ「今ある中で最小コストの結合」を1つ確定し、それを次の候補集合に組み込んで繰り返します。

# 貪欲法の骨子
集合 = {全表}
while 集合の表が 2 つ以上:
    集合内の全ペア(または中間結果と表のペア)の結合コストを見積もる
    最小コストのペアを結合し、その中間結果で2表を置き換える

各ステップで局所的に最良を選ぶだけなので計算量は N の多項式(おおむね N の3乗程度)に収まります。代償は局所最適に陥ることです。今すぐ安い結合が、後段で巨大な中間結果を招くこともあり、貪欲法は大域最適を保証しません。実装によっては複数の開始点から貪欲探索を走らせて最良を採るなど、品質を補う工夫を加えます。

遺伝的アルゴリズムによる近似

PostgreSQL は FROM 句の要素数が geqo_threshold(既定12)以上になると、DP をやめ**遺伝的アルゴリズム(GEQO)**に切り替えます(既定なら12表で切り替わる)。結合順序を「遺伝子」と見なし、自然選択を模した探索で近似最適解を探します。

GA の要素結合順序探索での対応役割
個体(染色体)1つの結合順序(表の並び)解の候補を表す
適応度その順序の推定総コストの逆数コストが低いほど生き残りやすい
選択適応度の高い個体を優先して残す良い順序へ集団を寄せる
交叉・突然変異順序の部分入れ替え・ランダム変更未探索の順序へ多様性を確保

GA は集団(個体の集まり)を世代交代させ、適応度の高い順序を交叉・突然変異で混ぜながら、限られた評価回数で「十分良い」順序を探します。全列挙も DP もしないため、最適は保証されない代わりに、表数が増えても探索時間を一定に抑えられるのが利点です。

GEQO は計画が不安定になりうる

GA は乱数を使うため、同じクエリでも実行のたびに別の計画が選ばれることがあります。性能の再現性が要る本番では、geqo_threshold を引き上げて DP を使う、結合の書き方で探索空間を縮めるなどの対処を検討します。逆に表数が極端に多くプランニング自体が遅すぎる場合は、GEQO の閾値を下げてプランニング時間を優先します。

手法の使い分け

手法計算量の目安最適性向く規模
全列挙N!(左深木)〜超指数厳密に最適ごく少数の表のみ
動的計画法約 3 の N 乗(左深木の範囲で)最適中規模(〜十数表)
貪欲法N の多項式局所最適・保証なし大規模で高速に決めたい
遺伝的アルゴリズム評価回数で制御可能近似・保証なしDP が破綻する多表結合

実用のオプティマイザは段階的に切り替えます。表数が少なければ DP で厳密に近い最適解を求め、閾値を超えたらヒューリスティック(貪欲法や GA)に落として「最適は捨て、現実的な時間で十分良い解」を得る、という設計です。どこで切り替えるかは、プランニングに使ってよい時間と、誤った計画が招く実行時間の損失とのトレードオフで決まります。

試験・面接での問われ方

「なぜオプティマイザは結合順序を全列挙しないのか」には、候補数が表数に対し階乗的(左深木)〜超指数的(ブッシー木)に増えるため、と答えます。続けて「動的計画法は部分集合ごとに最良プランを共有し約 3 の N 乗 に落とす」「表数が閾値を超えると貪欲法や遺伝的アルゴリズムへ切り替えて近似する」と述べれば、原理とトレードオフを押さえた回答になります。

まとめ

  • 結合順序の候補は左深木で N! 通り、ブッシー木では超指数的に爆発する。中間結果の行数が順序で桁違いに変わるため、順序選択は最適化の核心。
  • System R 流の DP は「部分集合ごとに最良プランを1回だけ計算して再利用」する最適部分構造の利用で、計算量を約 3 の N 乗 に圧縮する。
  • それでも DP 表は 2 の N 乗 でメモリが膨らむため、大規模では貪欲法(局所最適・多項式時間)や遺伝的アルゴリズム(近似・時間を制御可能)へ切り替える。
  • いずれの手法もコスト比較はカーディナリティ推定に依存し、多表ほど推定誤差が増幅する。統計の鮮度が、どの探索手法を使っても計画品質の前提になる。

データベース Article

結合順序探索:動的計画法と遺伝的/貪欲法を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

クエリ最適化

比較で見る軸

難易度: advanced / カテゴリ: データベース / タグ数: 5

導入後に効く点

System R 流のボトムアップ動的計画法は部分集合ごとに最良プランを1回だけ求めて再利用し、計算量を N! から約 3 の N 乗(部分集合×分割)へ落とす。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
データベース
タグ数
5

判断チェックリスト

  • 自社の用途が「クエリ最適化 / 結合順序」に近いか確認する。
  • 強みである「N 表の結合順序は組合せ爆発する。左深木に限っても N! 通り、ブッシー木まで含めると候補数は超指数的に増え、十数表で全列挙は非現実的になる。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

クエリ最適化結合順序動的計画法オプティマイザ遺伝的アルゴリズムクエリ最適化結合順序動的計画法
参考: 公式情報