skip to content

Compare equi-width and equi-depth histograms as used by a query optimizer. Which handles skewed data better, and why do engines usually store one alongside a most-common-value list?

level: middleimportance: should knowfreq 36%

answer

  1. Equi-width: equal value span, unbounded error under skew
  2. Equi-depth: equal row count per bucket, bounded error
  3. Equi-depth needs sorting; equi-width is a counting pass
  4. Can't split a single repeated value ⇒ MCV list
  5. Top/bottom buckets age fastest on increasing columns

basics

~20 s

An equi-width histogram splits the value range into buckets of equal width, so a dense region lands in one huge bucket. An equi-depth (equi-height) histogram splits so every bucket holds about the same number of rows, giving narrow buckets where data is dense. Equi-depth handles skew far better, which is why engines prefer it, with an MCV list handling single dominant values it cannot represent.

solid answer

~60 s

An **equi-width** histogram divides the value range into buckets of equal span — 0-100, 100-200, and so on — and records how many rows fall in each. It is trivial to build, but under skew one bucket can hold most of the table while others are empty, and estimates inside that bucket assume a uniform spread that is completely wrong. An **equi-depth** (equi-height) histogram instead chooses boundaries so each bucket contains roughly the same number of rows. Dense regions get many narrow buckets, sparse regions few wide ones. Error is bounded by the bucket size: with 100 buckets, no bucket represents more than about 1% of the table, so any range estimate is off by at most a fraction of that regardless of shape. That is the property that makes equi-depth the mainstream choice. Equi-depth still struggles with a **single value repeated across many buckets** — a dominant value has no interior boundary to sharpen. That is why engines pair the histogram with an MCV list: the MCVs absorb the heavy hitters, and the histogram is built over what remains.

code

text · 9 lines
text
values: mostly 0-50, a long thin tail up to 1000

equi-width (10 buckets, equal span):
  [0,100)=950k  [100,200)=30k  [200,300)=8k  ... [900,1000]=12
  -> estimating amount < 20 interpolates inside a 950k-row bucket

equi-depth (10 buckets, ~100k rows each):
  bounds: 0, 5, 9, 14, 19, 26, 35, 51, 90, 240, 1000
  -> amount < 20 covers ~4 full buckets: error bounded by one bucket

go deeper

for a junior

Define both: equal value span versus equal row count per bucket, and say equi-depth copes with skew.

for a middle

Explain the bounded-error property of equi-depth and why range predicates need shape information that a distinct-value count cannot give.

for a senior

Add the MCV-plus-histogram split, bucket-count tradeoffs, and the stale-last-bucket failure on increasing columns.

for a principal

Frame histogram resolution and per-column targets as a cost/benefit policy — catalog size and gathering cost against plan stability on the queries that actually matter.

## Why a histogram exists A distinct-value count only answers equality predicates, and only under a uniformity assumption. Range predicates — `created_at > ?`, `amount BETWEEN ? AND ?` — need to know the *shape* of the distribution. A histogram is a compact approximation of that shape: a small set of buckets, each summarizing a slice of the value domain. ## Equi-width Divide the range from min to max into `k` buckets of equal span, then count how many rows land in each. For `amount` from 0 to 1,000 with 10 buckets: 0-100, 100-200, ... 900-1000. - **Cheap to build and update**; boundaries are known in advance, so counting is a single pass with no sorting. - **Terrible under skew.** If 95% of orders are under 50, the first bucket holds 95% of rows and the other nine are near-empty. Estimating `amount < 20` requires interpolating inside that first bucket, and the interpolation assumes rows are spread evenly across 0-100 — which is exactly the assumption the data violates. The error is unbounded: the estimate can be off by an arbitrary factor depending on how the mass sits inside the bucket. - **Sensitive to outliers.** A single row with `amount = 10,000,000` stretches the range, so every real value is crushed into the first bucket. ## Equi-depth (equi-height) Sort (or sample and sort) the values and choose boundaries so each bucket holds approximately the same number of rows — `n/k` each. Store the boundary values. For the same skewed `amount` column with 10 buckets, boundaries might be 0, 5, 9, 14, 19, 26, 35, 51, 90, 240, 1000 — narrow where data is dense, wide where it is sparse. - **Bounded error.** Every bucket carries the same known row count, so any range predicate is estimated as (whole buckets covered) x n/k, plus interpolation inside at most two partial end buckets. With 100 buckets, the interpolation error is confined to under 2% of the table regardless of distribution shape. That guarantee is distribution-independent, which is the whole point. - **More expensive to build** — requires sorting or quantile estimation over a sample rather than a plain counting pass. - **Adapts automatically** to whatever shape the data has, including multi-modal distributions. This is why essentially every mainstream cost-based optimizer uses equi-depth histograms (sometimes under names like *height-balanced* or *quantile* histograms). ## Where equi-depth still fails, and why MCV lists exist Equi-depth assumes it can place boundaries wherever it likes. It cannot split a single repeated value. If one value covers 40% of the table, that value spans many consecutive buckets, and inside them the histogram has no resolution at all — it cannot distinguish that value from its neighbours, and estimates for nearby values inherit the distortion. The standard fix is to handle frequent values separately: 1. Extract the top-k values by frequency into an **MCV list** with exact measured frequencies. 2. Build the histogram over **only the remaining rows**. Now equality on a heavy hitter is answered exactly from the MCV list, and the histogram describes a much smoother residual distribution, so range estimates improve too. This split — MCV list plus equi-depth histogram over the remainder — is the shape you will find in most modern engines, and describing it is what separates a solid answer from a textbook one. ## Bucket count and its cost More buckets means finer resolution and better estimates, at the cost of larger catalog entries, more planning-time work to walk them, and a more expensive statistics-gathering pass (a larger sample is needed to place many boundaries reliably). Defaults are typically in the 100-256 bucket range. Raising the target is a per-column decision worth making for a few known-skewed, heavily-filtered columns — not a global change. ## Practical notes - Histograms are usually built from a **sample**, so boundaries are approximate and small errors are normal. - The **first and last buckets** age fastest: on a monotonically increasing column such as a timestamp or sequence id, new rows fall beyond the recorded maximum, and queries about "recent" data estimate near zero rows until statistics are refreshed. This is the single most common histogram-staleness bug in production. - A histogram describes **one column**. Two histograms cannot tell you anything about the joint distribution of two correlated columns.

  • Why does an equi-depth histogram bound the estimation error while an equi-width one does not?
    Because each equi-depth bucket carries a known, equal number of rows, a range predicate is answered by counting whole buckets, with interpolation needed only inside the two partial end buckets. The maximum error is therefore about one bucket's worth of rows, independent of distribution shape. In an equi-width histogram a single bucket may hold most of the table, so interpolation error inside it is unbounded.
  • Why not simply raise the bucket count until estimates are perfect?
    Buckets cost catalog space, planning time to traverse, and a larger sample during statistics gathering to place boundaries reliably. Beyond a few hundred buckets the accuracy gain flattens while the cost keeps rising. Raising the target is worth doing selectively for a small number of known-skewed, heavily-filtered columns.
  • Which histogram bucket goes stale first on an ever-increasing timestamp column?
    The last one. New rows land beyond the recorded maximum value, so predicates asking for recent data fall outside the histogram entirely and estimate close to zero rows. The optimizer then picks a plan sized for a handful of rows and gets millions — a very common production failure that a statistics refresh fixes immediately.

Equi-width is cutting a country into equal-area squares to count people; equi-depth is drawing electoral districts of equal population — tiny in cities, huge in the countryside.

saying these in an interview costs you the question

  • Saying equi-width handles skew better because the buckets are 'evenly spread'
  • Believing a histogram captures relationships between two columns
  • Thinking equi-depth buckets can isolate a single dominant value without an MCV list
  • Assuming histograms are built from a full scan rather than a sample
  • Proposing a global increase in bucket count as a general performance fix

context