Skip to content

Missing WHERE clause

Claim: Performance

A top-level SELECT that reads from a table without a WHERE clause returns every row. For small lookup tables this is fine, but for large tables it means a full sequential scan — reading the entire table regardless of how many rows the caller actually needs.

-- Reads every row from a potentially large table
SELECT id, name FROM customers;

Without a WHERE clause, PostgreSQL has no choice but to scan the whole table. This means:

  • I/O proportional to table size — every page in the table is read from disk or shared buffers
  • No index usage — indexes only help when there’s a predicate to filter on
  • Network transfer — all rows are sent to the client, which can be slow for large tables

For tables with millions of rows, an unfiltered SELECT can lock up a connection for seconds or longer and put heavy load on shared buffers.

Unfiltered selects are reasonable for:

  • Small reference or configuration tables (countries, status codes, feature flags)
  • Aggregations that genuinely need all rows (SELECT count(*) FROM orders)
  • Data exports or ETL pipelines designed to process full tables

Add a WHERE clause to filter to the rows you need:

SELECT id, name FROM customers WHERE active = true;

If you need to browse large result sets, combine with pagination:

SELECT id, name FROM customers WHERE active = true ORDER BY name LIMIT 50 OFFSET 0;

Query Doctor reports this Finding on a top-level SELECT with no WHERE clause when its FROM reads a table’s rows unfiltered. A table listed directly in FROM is read unfiltered. A CTE counts only when its own query reads a table unfiltered. A CTE with a WHERE, a LIMIT or a single-row aggregate does not, and neither does a recursive CTE whose every arm is filtered. A FROM that holds only subqueries is not reported.

A whole-table aggregate such as SELECT count(*) FROM orders is not reported, because it needs every row by design. An aggregate with GROUP BY is.

The Finding’s marker points to the first table in the FROM clause.