skip to content

Query Processing & Optimization

How a relational engine turns my SQL text into results: parsing, rewriting, cost-based planning, and physical execution. Interviewers probe this axis to see whether I can reason about why a query is slow from the engine's point of view, not just tweak the SQL.

part ofRelational database conceptsoverview, primer and where to startread it →
on this pageshow

explore

questions

72 · 4 sections

What makes a subquery "correlated", and why does a correlated subquery risk being executed once per row of the enclosing query?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A correlated subquery references a column from the enclosing query, so its result depends on the current outer row and logically must be re-evaluated for each one. An uncorrelated subquery is self-contained: computed once, reused for every row.

open as a page

In a query plan, what does it mean to "push a predicate down", and what does the optimizer gain by doing it? Include projection pushdown in your answer.

level: juniorimportance: must knowfreq 70%
basics
~20 s

Pushdown moves a filter as close to the data source as possible - below joins and into scans - so fewer rows travel up the plan. Projection pushdown does the same for columns: read and carry only the columns actually needed. Both cut I/O, memory and CPU.

open as a page

When an optimizer flattens a WHERE EXISTS or WHERE IN subquery, what join shape does it produce, and how does that shape differ from an ordinary inner join?

level: middleimportance: must knowfreq 68%
basics
~20 s

It produces a semi-join: for each outer row it checks whether at least one matching inner row exists, then emits the outer row once and stops probing. An inner join emits one output row per matching pair, so it can duplicate outer rows and it exposes inner columns.

open as a page

Under what condition may an optimizer legally convert a LEFT OUTER JOIN into an INNER JOIN, and why is that conversion worth making?

level: middleimportance: must knowfreq 52%
basics
~20 s

When a predicate applied after the join rejects NULLs on the null-supplying side, the NULL-extended rows cannot survive anyway, so the outer join is equivalent to an inner join. Converting is worth it because inner joins can be reordered freely and get more access-path and pushdown options.

open as a page

In a relational database, what is the difference between a logical plan and a physical plan for the same query, which component produces each, and why does the distinction exist at all?

level: middleimportance: must knowfreq 55%
basics
~20 s

A logical plan says what result is wanted - relational operators like scan, filter, join, aggregate - with no algorithms. A physical plan says how: chosen access methods, join algorithms and order, with costs. Binding and rewriting produce the logical plan; the cost-based optimizer produces the physical one.

open as a page

Relational database optimizers are commonly described as either rule-based or cost-based. Explain the difference, and why essentially every modern relational engine chose the cost-based design.

level: juniorimportance: must knowfreq 52%
basics
~20 s

A rule-based optimizer applies a fixed priority list of heuristics regardless of the data. A cost-based optimizer enumerates alternative plans, estimates the cost of each from statistics about the data, and picks the cheapest. Cost-based wins because the right plan depends on data distribution, which rules cannot see.

open as a page

What are optimizer statistics in a relational database, and what goes wrong when they are missing or out of date?

level: juniorimportance: must knowfreq 58%
basics
~20 s

They are summaries of the data — row counts, distinct values per column, null fractions, common values, value distributions — that the query planner reads to guess how many rows each step will produce. Missing or stale statistics make those guesses wrong, so the planner picks a bad access path or join method and the query runs far slower.

open as a page

A cost-based query optimizer must decide whether to reach a table through an index or read it whole. Explain how an estimated selectivity, derived from stored statistics, drives that decision.

level: middleimportance: must knowfreq 66%
basics
~20 s

The optimizer estimates what fraction of rows the predicate will match using stored statistics, converts that fraction into an estimated cost for each candidate path, and picks the cheapest. Low estimated selectivity favours the index; high favours the full scan.

open as a page

Explain what selectivity and cardinality mean to a query optimizer, how a predicate's selectivity is turned into an estimated row count, and why an error in an early estimate is more damaging than an error in the cost formula.

level: middleimportance: must knowfreq 58%
basics
~20 s

Selectivity is the fraction of rows a predicate keeps (0 to 1); cardinality is the resulting row count — input rows times selectivity. Every cost term scales with cardinality, and estimates feed into the next operator, so an early error multiplies up the plan tree and produces structurally wrong join and access choices.

open as a page

A single query joins eight tables. Why does the order in which the database engine joins them matter so much, and roughly how many possible orderings exist?

level: middleimportance: must knowfreq 55%
basics
~20 s

Join order never changes the result, but it changes the size of the intermediate results carried between operators, so cost can differ by orders of magnitude. The number of orderings grows factorially: eight tables give 40,320 left-deep orders and roughly 17 million bushy ones.

open as a page

The same amount of data is scanned by two queries, yet one starts returning rows almost immediately while the other returns nothing for several seconds and then delivers everything at once. What in the execution plan explains the difference?

level: juniorimportance: must knowfreq 48%
basics
~20 s

A fully pipelined plan passes each row up to the client as it is produced, so the first row arrives early. If the plan contains a blocking operator — a sort, a hash build, a grouping step — nothing can be emitted until that operator has read all its input, so results appear only at the end.

open as a page

Describe how a naive nested loop join produces its result, and what its cost is in terms of the sizes of the two inputs.

level: juniorimportance: must knowfreq 64%
basics
~20 s

For each row of the outer input, scan the entire inner input and emit the pairs that satisfy the join predicate. Comparisons are O(n*m), and the naive form re-reads the inner input once per outer row, so I/O is the real problem.

open as a page

When a database sorts or groups a large result set, the plan may report that the operator "spilled to disk". What does spilling mean, what triggers it, and how does it show up in query performance?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Each sort or grouping operator gets a limited memory budget. If the rows it must hold exceed that budget, the engine writes partial results into temporary files and reads them back later. Spilling replaces memory work with disk I/O, so latency jumps sharply.

open as a page

Explain how a database execution engine performs a hash join between two tables, describing what happens in each phase and when the optimizer is likely to choose this join method.

level: middleimportance: must knowfreq 70%
basics
~20 s

Phase one (build): read the smaller input fully and hash its join-key values into an in-memory hash table. Phase two (probe): stream the larger input, hash each row's key, look it up, and emit matches. It is chosen for equality joins over large inputs with no useful index.

open as a page

What is vectorized query execution, and why does processing a batch of rows per operator call typically run several times faster than processing one row per call?

level: middleimportance: must knowfreq 45%
basics
~20 s

Each operator call returns a batch — commonly around 1024 rows held column-wise — instead of one row. Per-call dispatch is amortized over the batch, and the inner loops become tight, branch-free, cache-resident and SIMD-friendly, so far more cycles go into real work.

open as a page

What does a database execution plan tell you, and what is the difference between a plan the optimizer only predicts and a plan collected while the query actually ran?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A plan is the tree of physical operators the engine will run - scans, joins, sorts - with estimated rows and cost per node. A predicted plan shows estimates only; an execution-time plan really runs the query and adds actual rows, loop counts and timings.

open as a page

Databases keep a cache of already-compiled execution plans. Explain what such a plan cache stores, what entries are keyed on, and why two statements that mean exactly the same thing but differ in whitespace or literal values usually get separate cache entries.

level: middleimportance: must knowfreq 52%
basics
~20 s

The cache stores compiled physical plans so repeated statements skip parsing, binding and optimization. Entries are keyed on the statement text (plus session context like schema search order and some settings), and the key is essentially exact-match, so different whitespace or inlined literals produce different keys and separate entries.

open as a page

How do you read an execution plan tree - which operator runs first and how does data flow between nodes - and how do you interpret a node reported as '8 rows, 40,000 executions'?

level: middleimportance: must knowfreq 68%
basics
~20 s

Read innermost/deepest nodes first: leaves produce rows, parents consume them, results flow upward to the root. A node showing 8 rows over 40,000 executions was started 40,000 times and produced about 8 rows each time - roughly 320,000 rows in total, so multiply before judging cost.

open as a page

A parameterized query normally returns in milliseconds but intermittently takes minutes for days at a time, with no schema change and no data-volume change, and it recovers as soon as the statement is recompiled. Explain the plan-reuse behaviour that causes this and how you would confirm it.

level: seniorimportance: must knowfreq 50%
basics
~20 s

The engine compiled the plan using the first parameter values it saw and cached it. If those values were unrepresentative - very selective or very unselective compared with typical ones - every later execution reuses a plan tuned for the wrong case. Recompiling with different values silently swaps which case suffers.

open as a page

In an execution-time plan, one operator estimated 50 rows but actually produced 2 million. What does that gap tell you, what typically causes it, and how does it make the query slow?

level: seniorimportance: must knowfreq 62%
basics
~20 s

It is a cardinality misestimate: the optimizer planned for a tiny input and got a huge one, so it likely chose the wrong join method, join order and memory grant. Causes are stale or missing statistics, correlated predicates, and predicates the optimizer cannot see through. Find the lowest node where the gap starts.

open as a page