indexing
Index Anti-patterns
Recognize patterns that silently disable indexes — implicit casts, leading wildcards, OR conditions, functions on columns.
The index exists, the query is still slow, and nothing reports an error. The result is correct; it just took thirty seconds.
A phone book sorted by last name; if you only know "Sm-something at the start", a wildcard search forces flipping through every page.
Key Concepts
1
This is nearly always the query being written in a way that makes the index unusable. One rule explains
most cases.
2
Keep the indexed column bare. An index stores the column's values. It does not store the values of
expressions you wrap around them.
3
WHERE YEAR(created_at) = 2026 -- index unusable
WHERE created_at >= '2026-01-01'
AND created_at < '2027-01-01' -- same rows, index used4
The index holds created_at values, not YEAR(created_at) values, so the first form forces the database to
compute the function for every row before it can compare. The second asks for a range of stored values,
which is exactly what a sorted index gives you.
created_atYEAR(created_at)
5
Same problem, different disguises:
6
WHERE price * 1.1 > 100 -- arithmetic on the column
WHERE LOWER(email) = 'a@b.com' -- function on the column
WHERE CAST(id AS TEXT) = '42' -- explicit conversion7
If you genuinely query by the expression, build an index on the expression itself.
8
The invisible cast. Compare an indexed text column to a number and most databases convert the column,
not the literal:
9
WHERE phone = 9876543210 -- phone is VARCHAR: converts every row
WHERE phone = '9876543210' -- index used10
Nothing warns you. The types silently disagree and the index silently drops out.
11
A leading wildcard has nowhere to start. LIKE '%gupta' gives no known prefix, so sorted order is
useless and the whole index is read. LIKE 'gupta%' is a range and works fine.
LIKE '%gupta'LIKE 'gupta%'
12
OR across different columns. WHERE email = ? OR phone = ? usually cannot use one index, since
neither one covers both branches. Some databases combine two indexes; many just scan. Writing it as a
UNION of two indexable queries often fixes it outright.
ORWHERE email = ? OR phone = ?UNION
13
Nothing is wrong and the scan is correct. If the predicate matches most of the table, scanning really is
faster. Before hunting for a bug, check how many rows the query returns.
14
How to tell which of these you have. Run EXPLAIN. You are looking for a sequential or full scan where
you expected an index seek. The plan says what actually happened; reasoning about the SQL says only what you
intended.
EXPLAIN
15
What the interviewer is probing.1. "Why does WHERE YEAR(created) = 2026 ignore the index?" Probing: the classic. Stalls: "It
should use it." Moves up: wrapping the column in a function means the stored values no longer
match what is being compared — rewrite as a range on created.
16
2. "What is wrong with indexing every column?" Probing: the cost. Stalls: "Nothing." *Moves
up:* writes update every index, storage grows, and the planner has more options to get wrong —
indexes should follow actual queries.
17
3. "Why is an index on a low-cardinality column often useless?" Probing: selectivity.
Stalls: "It still helps." Moves up: a boolean matching half the table means a scan is cheaper
than index lookups plus fetches — unless it is part of a composite or a partial index.
18
4. "What is the problem with a leading wildcard LIKE?" Probing: the ordering. Stalls:
"None." Moves up: LIKE '%term' cannot use a B-tree, because the index is ordered by prefix —
full-text or trigram indexing is the answer.