Cookbook recipe

Slow Query / Sequential Scan on Large Table

Applies to PostgreSQL 13–17 Last reviewed May 2026 Grounded in source
Estimated investigation4 min

Scenario

Scenario A backend engineer reports that a customer order lookup query that previously ran in milliseconds is now taking 8–12 seconds. The table has grown from 50,000 to 2 million rows over the past 3 months.…

Investigation Path

Scenario

A backend engineer reports that a customer order lookup query that previously ran in milliseconds is now taking 8–12 seconds. The table has grown from 50,000 to 2 million rows over the past 3 months. No schema changes were made.

How to Identify

Conditions:

  • Query running above acceptable threshold in pg_stat_statements
  • EXPLAIN output shows Seq Scan on a large table
  • High I/O visible in pg_stat_bgwriter (buffers_clean activity)
  • pg_stat_user_tables shows last_analyze is stale or NULL
  • Index exists on the filter column but idx_scan = 0 in pg_stat_user_indexes

Analysis Steps

-- 1. Identify slow queries
SELECT query, calls, total_exec_time, mean_exec_time, rows
FROM   pg_stat_statements
ORDER  BY total_exec_time DESC
LIMIT  10;

-- 2. Run EXPLAIN ANALYZE on the specific query
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM orders WHERE customer_id = 42;

-- 3. Check statistics freshness
SELECT schemaname, relname, last_analyze, last_autoanalyze,
       n_live_tup, n_dead_tup
FROM   pg_stat_user_tables
WHERE  relname = 'orders';

-- 4. Check if an index exists and if it is being used
SELECT indexrelname, idx_scan, idx_tup_read
FROM   pg_stat_user_indexes
WHERE  relname = 'orders';

-- 5. Check for type mismatch between column and predicate
SELECT attname, atttypid::regtype
FROM   pg_attribute
WHERE  attrelid = 'orders'::regclass AND attname = 'customer_id';

Pitfalls

  • Do not SET enable_seqscan = off in production — this forces index use even when the planner is correct to use a seq scan (e.g., returning 80% of the table). It hides root causes.
  • Do not add an index without checking the write/read ratio first. High-write tables pay index maintenance overhead on every INSERT/UPDATE/DELETE.
  • Do not confuse planning time with execution time in EXPLAIN output — a query can plan fast but execute slow, or vice versa.
  • Stale statistics can make the plan appear correct when looking at estimated rows — always compare rows (estimate) vs actual rows in EXPLAIN ANALYZE.

Resolution Approach

A sequential scan on a large table means PostgreSQL reads every row. The fix depends on root cause:

This is a Pro lesson

Get every Learning Pathway and cookbook recipe — grounded in PostgreSQL source code, with diagnostics, fixes, and prevention for each topic.

Continue this lesson to learn:

  • Mitigation Actions
  • All 36 Learning Pathway lessons
  • 170+ cookbook recipes
  • Source-grounded diagnostics & fixes

Secure checkout Cancel anytime Source-grounded

Career Impact

This scenario builds production judgment and operational confidence under pressure.

Open Career Dashboard →

Keep going

Related & next steps

Was this helpful?

← All cookbook recipes