All writing2026.09.22 · CS 411 · Database Systems · 2 min

Query Optimization: Cardinality Before Cost

Rule-based rewrites, selectivity estimates, join order, physical-plan cost, and a disciplined way to read estimation errors.

The optimizer cannot measure every possible plan before running a query. It estimates intermediate result sizes, assigns costs, and chooses the cheapest plan under its model.

Logical rewrites reduce work early

Relational-algebra equivalences allow the optimizer to:

  • push selections toward scans;
  • remove unused columns early;
  • reorder inner joins;
  • replace a Cartesian product plus filter with a join.

Pushing topic = 'databases' below a join can shrink the input before an expensive operator. Outer joins, limits, volatile functions, and NULL semantics restrict which rewrites are legal.

Cardinality drives every later choice

For equality selection under a uniformity assumption:

T(σ A=c (R)) ≈ T(R) / V(R,A)

For an equality join:

T(R ⋈ S) ≈ T(R) × T(S) / max(V(R,A), V(S,A))

T is tuple count and V is the number of distinct values. These formulas are useful models, not promises. Skew, correlated columns, missing values, and stale statistics violate their assumptions.

In plain terms: the optimizer chooses a route using a map of expected row counts. If one intersection is marked as ten cars but actually has ten thousand, every route decision after it may be wrong.

Join order changes intermediate size

With three tables, the final result can be identical while intermediate results differ by orders of magnitude. Joining two large tables first may create a huge temporary result; joining a selective relation first may reduce the rest of the plan.

Dynamic programming can compare subplans for smaller join sets, but the search space grows quickly. Optimizers prune alternatives and use heuristics for large queries.

Cost is more than elapsed time

The model combines sequential and random page access, CPU work, memory, parallelism, and expected output. Its cost units are for comparing plans; they are not milliseconds.

EXPLAIN (ANALYZE, BUFFERS, VERBOSE, TIMING OFF)
SELECT t.name, count(*)
FROM topic t
JOIN paper_topic pt ON pt.topic_id = t.topic_id
JOIN paper p ON p.paper_id = pt.paper_id
WHERE p.year >= 2024
GROUP BY t.name;

Read bottom-up and compare estimated rows with actual rows at the first point they diverge. A bad top-level estimate is often caused by an earlier scan or join, not the top node itself.

Fix evidence, not symptoms

When estimates are wrong, check:

  1. Were statistics collected after loading representative data?
  2. Is the test distribution realistic?
  3. Are predicates correlated while the model assumes independence?
  4. Is a cast or function hiding an indexed column?
  5. Is the query returning so much data that a scan is correct?

Hints can force a plan, but they freeze a decision that may become wrong as data changes. Improve statistics, query shape, and indexes first.

Review card

  • Rewrites must preserve semantics.
  • Intermediate cardinality determines downstream cost.
  • Uniformity and independence are assumptions, not facts.
  • Join order matters because intermediate results differ.
  • Planner cost is a comparison unit, not elapsed time.
  • Find the first estimate/actual divergence.
  • Prefer fixing evidence over forcing plans.

Optimizer 無法在執行前實測每個 possible plan,只能估算 intermediate result size、指派 cost,再從模型中選最便宜的計畫。

Logical rewrite 提早減少工作

Relational-algebra equivalence 讓 optimizer 可以:

  • 把 selection 推近 scan;
  • 提早移除不需要的 column;
  • 調整 inner join 順序;
  • 把 Cartesian product 加 filter 改寫成 join。

topic = 'databases' 推到 join 下方,可能在昂貴 operator 前就縮小 input。但 outer join、limit、volatile function 與 NULL semantics 都會限制合法 rewrite。

Cardinality 決定後面所有選擇

在 uniformity assumption 下,equality selection 可估成:

T(σ A=c (R)) ≈ T(R) / V(R,A)

Equality join 可估成:

T(R ⋈ S) ≈ T(R) × T(S) / max(V(R,A), V(S,A))

T 是 tuple count,V 是 distinct value 數量。公式是推理模型,不是承諾;skew、correlated column、missing value 與 stale statistics 都可能破壞 assumption。

白話來說: Optimizer 拿著預估車流量的地圖選路。某個路口標示十台車,實際卻有一萬台,後面每個路線決定都可能跟著錯。

Join order 改變 intermediate size

三張 table 最後結果可以完全相同,但 intermediate result 差好幾個數量級。先 join 兩張大表可能產生巨大 temporary result;先和 selective relation join,則能縮小後續工作。

Dynamic programming 可以比較較小 join set 的 subplan,但 search space 成長很快。大型 query 的 optimizer 必須 prune alternative 並使用 heuristic。

Cost 不等於 elapsed time

Cost model 會組合 sequential/random page access、CPU、memory、parallelism 與預期 output。Cost unit 用來比較 plan,不是 millisecond。

EXPLAIN (ANALYZE, BUFFERS, VERBOSE, TIMING OFF)
SELECT t.name, count(*)
FROM topic t
JOIN paper_topic pt ON pt.topic_id = t.topic_id
JOIN paper p ON p.paper_id = pt.paper_id
WHERE p.year >= 2024
GROUP BY t.name;

從下往上讀,在第一個 estimate rows 與 actual rows 分岔的位置停下來。Top-level estimate 很差,原因通常是更早的 scan 或 join,不是 top node 本身。

修 evidence,不只修 symptom

Estimate 錯誤時依序檢查:

  1. 載入代表性資料後有沒有收集 statistics?
  2. Test distribution 是否接近 production?
  3. Predicate 彼此 correlated,模型卻假設 independent 嗎?
  4. Cast 或 function 是否包住 indexed column?
  5. Query 是否真的要回傳大量資料,所以 scan 才是正解?

Hint 能強迫 plan,也會凍結一個可能隨資料改變而失效的決策。先改善 statistics、query shape 與 index。

複習卡

  • Rewrite 必須保留 semantics。
  • Intermediate cardinality 決定 downstream cost。
  • Uniformity 與 independence 是 assumption,不是事實。
  • Join order 重要,因為 intermediate result 大小不同。
  • Planner cost 是比較單位,不是 elapsed time。
  • 找第一個 estimate/actual 分岔點。
  • 優先修 evidence,不要先強迫 plan。