When a prepared statement is executed repeatedly with different parameter values, a database can either keep one parameter-independent plan or optimize afresh for each execution's values. Compare the two strategies, and describe how an engine can decide between them automatically.
answer
- generic = one plan, average selectivity, no per-call compile
- custom = per-execution optimize, sees real values
- adaptive rule: sample custom costs, compare with generic
- skew and long-running -> custom; uniform and high-rate -> generic
- splitting the statement beats choosing, when classes differ
basics
~20 sA generic plan is compiled once using average selectivity and reused for all values: cheap, stable, but never value-specific. A custom plan is optimized per execution using the actual values: accurate, but pays optimization cost every time. Engines can compare observed custom-plan costs against the generic plan's cost and switch when generic is not worse.
solid answer
~60 s**Custom plan:** optimize on every execution using that execution's parameter values. The optimizer sees the real value, uses histograms, and estimates selectivity accurately. You get the best plan for each call - at the price of full parse/bind/optimize CPU per execution, plus contention on shared cache structures at high concurrency. **Generic plan:** optimize once with the parameters treated as unknowns, using average selectivity from statistics. Compiled once, reused forever, near-zero per-execution overhead - but it cannot be right for a skewed predicate where different values deserve different access paths. **The decision rule engines use:** run custom plans for the first several executions, record their estimated costs, then compute a generic plan and compare its estimated cost against the average custom cost. If the generic plan is not meaningfully worse, switch to it permanently and stop paying optimization cost; otherwise keep planning per execution. Most engines also let an administrator force either mode. Rule of thumb: uniform data and high call rates favour generic; skewed predicates, wide value ranges and expensive long-running queries favour custom.
go deeper
State the two options plainly - one plan for all values versus a fresh plan each time - and that the tradeoff is compile cost versus plan accuracy.
Explain generic estimation from average selectivity, why skew breaks it, and roughly how an engine samples custom plans before adopting a generic one.
Give concrete selection criteria by workload shape, distinguish generic from sniffed-and-cached, and propose splitting the statement when execution classes genuinely differ.
Discuss the limits of estimate-versus-estimate heuristics, latency predictability as a first-class goal, and the operational cost of plan variability across a fleet.
## The underlying tension Plan reuse saves optimization CPU; value-specific planning buys plan quality. For a parameterized statement executed thousands of times per second, these pull in opposite directions, and the right choice depends entirely on how much the best plan varies across parameter values. ## Custom plans A custom plan is compiled for the specific values of one execution. The optimizer substitutes the actual values into the predicates before estimating, so it can consult histograms and most-common-value lists and derive a realistic row count for *this* call. - **Strength:** best possible plan per execution. Essential when selectivity varies by orders of magnitude across values, when the predicate involves ranges whose width varies, or when a value hits a most-common-value entry. - **Cost:** full optimization on every execution. For a three-way join this is milliseconds of CPU - trivial next to a two-minute report, ruinous next to a 0.2 ms primary-key lookup executed 20,000 times a second. - **Secondary cost:** compilation touches shared structures; heavy concurrent compiling produces latch/mutex contention that reduces throughput more than the raw CPU number suggests. ## Generic plans A generic plan treats each parameter as an unknown and estimates using aggregate statistics - for equality on a column with `n` distinct values, roughly `rows/n`; for an unbounded range, a fixed default fraction. - **Strength:** compiled once and reused, so per-execution overhead is essentially the cost of execution alone. It is also *stable*: latency does not depend on which value happened to compile the plan, so behaviour is predictable and capacity planning is easier. - **Weakness:** on a skewed column the average is a fiction that describes no real value. A generic plan may choose a full scan that is right for the one dominant value and wasteful for the thousands of rare ones, or vice versa. ## How an engine chooses automatically A well-known adaptive approach: for the first several executions of a prepared statement, always build custom plans and remember each one's *estimated* cost. After that sample, build a generic plan and compare its estimated cost against the average of the custom costs, adding the known optimization cost to the custom side of the ledger. If the generic plan is not meaningfully more expensive, adopt it permanently - the engine has evidence that value-specific planning is not buying anything. If it is clearly worse, keep planning per execution. The elegance is that the decision is made from measurements of the actual statement rather than from a global setting, and it is conservative: uniform data quickly converges to generic, while skewed data keeps paying for accuracy. Most engines also expose a manual override to force one mode, which is how you respond to an incident quickly while designing the real fix. Note what the heuristic cannot see: it compares *estimated* costs. If the statistics that produce those estimates are wrong, both sides of the comparison are wrong, and the engine may adopt a generic plan that its own cost model wrongly believes is fine. ## Relationship to parameter sniffing These are three distinct behaviours, and interviewers listen for the distinction: - **Sniffed-and-cached:** compile once *using the first values seen*, then reuse. The plan is value-specific but for someone else's value - the classic bimodal-latency failure. - **Generic:** compile once using *no* values. Nobody's plan is ideal, but nobody's plan is arbitrary either; the behaviour is uniform and predictable. - **Custom every time:** compile per execution with each call's values. Always appropriate, always paid for. A generic plan is therefore one of the standard *remedies* for a sniffing problem: it removes the dependence on which value happened to compile first, at the price of ceiling performance. ## Choosing in practice Favour **generic** when: the predicate columns are close to uniformly distributed; the statement runs at high frequency with short execution times; predictable latency matters more than peak speed; or optimization CPU is already a visible share of server load. Favour **custom** when: predicates hit skewed columns such as status flags, tenant IDs, or soft-delete markers; parameter values include ranges of hugely varying width; the statement is long-running so optimization time is noise; or the cost gap between the right and wrong plan is catastrophic rather than marginal. And note the third option that beats both: if two classes of executions genuinely deserve different plans, giving them different statement texts gives each its own cache entry and its own stable plan, with one compile each. That converts a runtime dilemma into a design decision - usually the answer worth proposing at senior level.
- Why is comparing the estimated cost of a generic plan against custom plans not a perfect decision rule?Because both numbers come from the same cost model fed by the same statistics, so if the statistics misrepresent the data both estimates are wrong in the same direction and the comparison is meaningless. It also compares estimates rather than measured runtimes, so a generic plan that the model believes is only slightly worse can be far worse in practice. It is a good heuristic precisely because it is cheap, not because it is sound.
- When is splitting one statement into two better than choosing between generic and custom plans?When the executions fall into a small number of known classes with genuinely different optimal plans - for example a dominant tenant versus everyone else, or an unbounded date range versus a narrow one. Two statement texts get two cache entries, each compiled once and each stable, so you pay one compile per class and get a good plan for both. It also makes the intent explicit in the application rather than hiding it in optimizer heuristics.
saying these in an interview costs you the question
- Saying a generic plan is 'the plan without statistics' - it uses statistics, just not the parameter values.
- Assuming custom plans are always better while ignoring per-execution optimization cost and cache contention.
- Conflating generic plans with parameter sniffing; a generic plan deliberately does not look at values.
- Believing the adaptive choice is based on measured runtimes rather than estimated costs.
- Never mentioning that splitting the statement can remove the dilemma entirely.