Concept

SQL Server Statistics and Cardinality Estimation

By SprocOptimizer Engineering · Updated June 4, 2026 · 9 min read

SQL Server statistics are objects that describe how values are distributed in a column or index. The query optimizer uses them for cardinality estimation — predicting how many rows each step of a query will produce — and those estimates drive join types, memory grants, and the whole execution plan.

When statistics are outdated, the estimates drift from reality and the optimizer picks poor plans, even for well-indexed queries.

Statistics are the data the optimizer reasons from. Get them wrong and every downstream decision is built on a bad guess. This guide explains how estimation works, what stale statistics do to a plan, and how to keep them honest.

What statistics are

A statistics object summarizes the distribution of values in a column or index, primarily as a histogram — a compact description of how many rows fall into each range of values. SQL Server creates and maintains many of these automatically. They are small, but they are what the optimizer consults to answer the question that governs every plan: how many rows will this step return?

Why estimates drive everything

Cardinality estimation is the single most influential input to plan quality. From the estimated row counts, the optimizer decides:

Get the estimate right and these choices tend to be right. Get it wrong and they cascade into a slow plan.

What goes wrong: stale statistics

When statistics fall out of date, estimates and reality diverge. The optimizer might expect 10 rows and get 10 million, choose nested loops where a hash join was needed, or grant too little memory and spill to tempdb. As practitioners note, when statistics are stale or inaccurate even a well-indexed query can suddenly degrade.

The tell is visible in the execution plan: a large gap between estimated and actual rows. That same gap is also a hallmark of parameter sniffing — check both.

Keeping statistics current

SQL Server updates statistics automatically when enough rows change, and starting with SQL Server 2016 (compatibility level 130) it updates them more frequently for large tables. When you need a manual refresh:

Manual statistics maintenance-- Refresh one table's statistics (FULLSCAN reads every row; accurate but heavier)
UPDATE STATISTICS dbo.Orders WITH FULLSCAN;

-- Refresh all statistics in the current database
EXEC sp_updatestats;

Updating statistics is not a cure-all. As Erik Darling and others point out, sometimes the fix is a better index, a query rewrite, or a different predicate pattern — not just fresher stats. Treat stale statistics as one suspect, not the automatic culprit.

How automated analysis uses statistics

Statistics histograms are part of the evidence an AI analysis step needs to reason about a plan: they explain why the optimizer estimated what it did. SprocOptimizer collects statistics alongside DDL, indexes, and execution plans (all metadata — never row-level data) so its analysis can distinguish a stale-statistics problem from an indexing or SARGability problem, and recommend the fix that actually addresses the cause — validated before promotion.

Frequently asked questions

Statistics are objects that describe the distribution of values in a column or index — most importantly a histogram of how many rows fall into each range of values. The query optimizer reads them to estimate how many rows a query step will touch, which it needs in order to choose a good execution plan.

Cardinality estimation is the optimizer's prediction of how many rows each operation in a query will produce, based on statistics. Those estimates determine which join algorithm is chosen (nested loops, hash, or merge), how much memory is granted, whether the plan goes parallel, and which operators are used. Accurate estimates lead to good plans; inaccurate ones lead to bad plans.

When statistics are stale, the optimizer's row estimates no longer match reality, so even a well-indexed query can suddenly get a poor plan — the wrong join type, an undersized memory grant that spills to tempdb, or a scan where a seek was warranted. A large gap between estimated and actual rows in the execution plan is the classic symptom of an estimation problem.

SQL Server updates statistics automatically when enough rows change, and starting with SQL Server 2016 (compatibility level 130) it updates them more frequently for large tables. When you need a manual refresh, use UPDATE STATISTICS on a table or specific statistic, or sp_updatestats for a whole database. Note that updating statistics does not fix every problem — sometimes a better index or a query rewrite is what makes estimation accurate.

Primary sources & further reading

  1. Microsoft Learn — Cardinality Estimation (SQL Server).
  2. Erik Darling (Darling Data) — A little about out-of-date statistics in SQL Server.
  3. MSSQLTips — Manually Update Statistics in SQL Server.

Know whether it's stats, indexes, or sniffing

SprocOptimizer reads statistics, indexes, and plans together to pinpoint the real cause — then validates the fix before it ships, on-premises, with no row-level data leaving your network.

Request a Demo Read the Optimization Guide