Study Vault Search notes, aliases and full text
Graph
ConceptDBISAI-written · not yet proofread

Query Optimization

Query optimization finds the join order of minimal total cost for a set of tables by computing the optimal cost cost*(S) of every joinable subset S bottom-up with dynamic programming over partitions into two smaller subsets.

Formula#

Join graph. Undirected graph with one node per input table and an edge between two tables iff they can and should be joined according to the query. Two plans with table sets S1,S2S_1, S_2 are joinable iff S1∩S2=∅S_1 \cap S_2 = \emptyset and the join graph has an edge between a table of S1S_1 and a table of S2S_2.

Cost model (total cost; input tables have cost 0):

size(A⋈B)=min⁡{size(A),  size(B)}\text{size}(A \bowtie B) = \min\{\text{size}(A),\; \text{size}(B)\}

cost(A⋈B)=cost(A)+cost(B)+size(A⋈B)\text{cost}(A \bowtie B) = \text{cost}(A) + \text{cost}(B) + \text{size}(A \bowtie B)

Optimal cost (recursion over partitions):

cost∗(S)=min⁡S1∪S2=S,S1∩S2=∅S1≠∅,S2≠∅S1,S2 joinable{cost∗(S1)+cost∗(S2)+size(S1⋈S2)},cost∗({T})=0.\text{cost}^*(S) = \min_{\substack{S_1 \cup S_2 = S,\; S_1 \cap S_2 = \emptyset \\ S_1 \neq \emptyset,\; S_2 \neq \emptyset \\ S_1, S_2 \text{ joinable}}} \left\{ \text{cost}^*(S_1) + \text{cost}^*(S_2) + \text{size}(S_1 \bowtie S_2) \right\}, \qquad \text{cost}^*(\{T\}) = 0.

The recursion terminates because S1S_1 and S2S_2 are strict subsets of SS.

Dynamic programming in rounds. Plain recursion recomputes the same subsets over and over and takes exponential time, like the naive recursion F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2) for Fibonacci numbers (O(2n)O(2^n), while computing bottom-up takes linear time). Instead compute cost∗\text{cost}^* bottom-up:

  1. Round 1: one singleton plan per input table, cost 0.
  2. Round 2: all joinable pairs of input tables.
  3. Round kk: for k1=1,…,⌊k/2⌋k_1 = 1, \ldots, \lfloor k/2 \rfloor and k2=k−k1k_2 = k - k_1, combine every optimal plan of size k1k_1 with every joinable optimal plan of size k2k_2; keep the cheapest plan per table set.
  4. Round nn: yields cost∗(S)\text{cost}^*(S) for the set SS of all input tables.

Properties#

  • Only reachable subsets: in round kk, do not enumerate all subsets of size kk; only combine plans from earlier rounds whose union has size kk. Subsets that are not connected in the join graph never appear.
  • Joinability check: the lecture code uses a nested loop in O(∣S1∣⋅∣S2∣)O(|S_1| \cdot |S_2|); in practice one uses bit masks for the table sets.
  • Size is plan-independent: with this cost model, size(S1⋈S2)\text{size}(S_1 \bowtie S_2) is the minimum size of the tables in SS, whatever the split. The plans differ only in the cost of their intermediate results, so it pays to join the smallest table first.
  • Scope: only binary joins are optimised; selections, projections and the choice of join algorithm (hash join, Merge Join, index lookup via B+ Tree) are not part of this model.
# public/code/lecture-06/query-planning.py (excerpt)
for k in range(2, len(self.input_plans) + 1):
    optimal_plans[k] = {}
    for k1 in range(1, k // 2 + 1):
        k2 = k - k1
        for plan1 in optimal_plans[k1].values():
            for plan2 in optimal_plans[k2].values():
                if self.join_graph.joinable(plan1, plan2):
                    plan = QueryPlan.join(plan1, plan2)  # size = min, cost = c1 + c2 + size
                    if (plan.tables not in optimal_plans[k]
                            or plan.cost < optimal_plans[k][plan.tables].cost):
                        optimal_plans[k][plan.tables] = plan

Example#

Sources#

Search

Type In
↑ ↓ move ↵ open ⇥ next section Ctrl↵ results page Prefix # to search text only