Reading performance & plans
Concept  ·  SQL & Querying

A function-wrapped predicate (WHERE date(ts) = ..., UPPER(email) = ...) defeats index/partition pruning — rewrite it as a range.

An index or partition can only prune when the column appears bare in the predicate. Wrap it in a function — CAST(ts AS DATE), UPPER(email) — and the planner must evaluate every row. The rows are identical; the plan is not.

See it break
logins
idts
12024-03-05 08:00
22024-03-05 22:00
32024-03-06 01:00
The trap
SELECT
  id
FROM
  logins
WHERE
  CAST(ts AS DATE) = DATE '2024-03-05'
  ORDER BY
  id;
The fix
SELECT
  id
FROM
  logins
WHERE
  ts >= TIMESTAMP '2024-03-05'
  AND ts < TIMESTAMP '2024-03-06'
ORDER BY
  id;
The trap
1
2
The fix
1
2

Both return the same two logins — but wrapping the column in CAST(ts AS DATE) hides it from the index, so the query scans every row instead of pruning.

The rule

Rewrite the predicate so the column stands alone: a half-open timestamp range instead of CAST(ts AS DATE); a stored lowercased column or a functional index instead of UPPER().

order_ts >= DATE '2024-06-15' AND order_ts < DATE '2024-06-16'

Engines with functional/expression indexes can index the wrapped form directly; without one, the bare-column rewrite is the portable fix.

Shows up elsewhere
BETWEEN on timestamps — the midnight trap
Try one →Prove it in practice →