SUMMARY:
Learn why PostgreSQL chooses sequential scans, when they are efficient, and how to determine whether a Seq Scan is really a performance problem.
Table of contents
Introduction
When troubleshooting PostgreSQL performance, seeing a Seq Scan in an execution plan often triggers an immediate reaction:
“We need an index.”
But a sequential scan is not automatically a performance problem. In many cases, PostgreSQL chooses it because it is the most efficient execution plan for the query.
The goal of query optimization is not to eliminate sequential scans. The goal is to make sure PostgreSQL is choosing the right execution plan for the workload.
What Is a Sequential Scan?
A sequential scan means PostgreSQL reads the table from beginning to end and checks rows against the query condition.
For example:
SELECT *
FROM customers
WHERE country = 'USA';
If the table contains 10 million rows and 8 million customers are from the USA, PostgreSQL may decide that reading the entire table is cheaper than using an index.
Even if an index exists on country, PostgreSQL would need to find millions of matching entries through the index and then retrieve those rows from the table. At that scale, the index may provide little benefit.
A sequential scan can therefore be the correct choice.
Selectivity Matters
One of the most important factors is selectivity, which describes how many rows match the query.
Consider:
SELECT *
FROM customers
WHERE customer_id = 12345;
If the table contains 10 million rows and this query returns one row, an index is extremely useful.
The situation is different for:
SELECT *
FROM customers
WHERE status = 'ACTIVE';
If 90% of the table contains ACTIVE customers, PostgreSQL may prefer a sequential scan because the query needs most of the table anyway.
In simple terms:
- Small result set: Index Scan may be better
- Large result set: Sequential Scan may be better
PostgreSQL Uses Cost Estimates
PostgreSQL uses a cost-based optimizer. It estimates the cost of different execution strategies and chooses the plan it expects to be cheapest.
The optimizer considers factors such as:
- Table size
- Number of matching rows
- Data distribution
- Statistics
- I/O costs
- Available indexes
This is why having an index does not guarantee that PostgreSQL will use it.
PostgreSQL is not trying to use an index. It is trying to execute the query efficiently.
Statistics Can Change Everything
PostgreSQL relies on table statistics to estimate how many rows a query will return.
For example:
Estimated rows: 100
Actual rows: 2,000,000
This is a major estimation error and can lead to a poor execution plan.
To investigate, use:
EXPLAIN (ANALYZE, BUFFERS)
SELECT *
FROM customers
WHERE country = 'USA';
Pay particular attention to the difference between estimated rows and actual rows.
If statistics are outdated, running:
ANALYZE customers;
can help PostgreSQL make better decisions.
Sequential Scan vs. Bitmap Scan
PostgreSQL also has a middle-ground option called a Bitmap Heap Scan.
A bitmap scan can be useful when a query returns more rows than a traditional index scan can efficiently handle, but not enough rows to justify scanning the entire table.
This gives PostgreSQL multiple ways to access data rather than forcing it into an index or sequential scan decision.
Don’t Force PostgreSQL to Use an Index
A common mistake is trying to change planner settings simply because PostgreSQL is choosing sequential scans.
For example, changing random_page_cost may make PostgreSQL favor index scans, but that does not mean the resulting plans will be better.
Planner settings should reflect the actual environment, not be used simply to force a preferred execution plan.
When Should You Investigate a Sequential Scan?
A sequential scan deserves attention when:
- The table is very large.
- The query returns only a small number of rows.
- The query runs frequently.
- The query performs significant disk I/O.
- Estimated and actual row counts differ significantly.
- The query is contributing to overall database performance problems.
The important point is that Seq Scan by itself is not evidence of a problem.
Final Thoughts
A sequential scan is simply one of PostgreSQL’s execution strategies. Sometimes an index is the right answer. Sometimes updated statistics or query changes are needed. And sometimes the sequential scan is already the optimal plan.
So the next time you see:
Seq Scan on customers
Don’t immediately create an index.
Instead, ask:
“Why did PostgreSQL choose this plan, and is it actually causing a performance problem?”
That question leads to better troubleshooting and better PostgreSQL databases.