Find tables with heavy sequential scans
pg_seq_scan_tablesFind PostgreSQL tables with high sequential scans versus index scans to spot missing-index candidates. Pair with query analysis to identify the offending queries.
Instructions
Tables with high sequential-scan counts relative to index scans - the first place to look for missing-index candidates. Returns {rows, stats_reset, stats_reset_age_seconds}: each row has seq_scans, idx_scans, live tuples, and the ratio. A high ratio on a large table usually means a query is reading the whole table where an index would suffice. Pair with pg_top_queries to find which query is doing it.
These counters are cumulative since the last statistics reset, so every ratio here is only meaningful relative to the top-level stats_reset (and stats_reset_age_seconds). A ratio measured over a window that was reset minutes ago describes that window, not the workload; stats_reset: null means the start of the window is unknown.
On PostgreSQL 16+ each row also carries last_seq_scan and last_idx_scan timestamps (null = no such scan since the reset), which separate 'scanned hard months ago' from 'being scanned right now' in a way the raw counts cannot.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return (default 20). | |
| schema | No | Limit to one schema. If omitted, all user schemas are included. | |
| minSize | No | Minimum live tuple count to include (default 1000, filters out tiny/empty tables). |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| rows | Yes | ||
| _warnings | No | ||
| stats_reset | Yes | Every counter in `rows` is cumulative SINCE this point. Null = start of the window unknown. | |
| stats_reset_age_seconds | Yes | Seconds since `stats_reset`; null whenever that is null. |