CONCEPT Cited by 1 source
Wildcard-prefix non-sargable predicate¶
A wildcard-prefix LIKE predicate — a LIKE pattern
that begins with % or _ (e.g. '%ron', '%oh%')
— cannot use a B+tree index, so a WHERE name LIKE
'%ron' query falls back to a full table scan even when a
name index exists. This is structural, not a planner
heuristic: the index's sort order cannot answer suffix
queries.
(Source: sources/2026-04-21-planetscale-why-isnt-mysql-using-my-index.)
The one-line rule¶
MySQL can use the index until it reaches the first wildcard character. The index is not considered if the search string starts with a wildcard character.
— Aaron Francis, Why isn't MySQL using my index?
A wildcard at the start of the pattern is disqualifying. A wildcard anywhere after the first literal character still permits index use (as a range scan up to the wildcard, then a filter pass on the candidates).
Why the B+tree can't answer suffix queries¶
A secondary B+tree on a string column is sorted
lexicographically by prefix — all entries starting with
'Aa' are adjacent, all starting with 'Ab' are adjacent,
and so on. Navigating the tree requires knowing the
leading characters to pick the right child page.
LIKE 'Aa%'supplies the leading'Aa'→ the planner seeks to that subtree and walks it in order. WorkedEXPLAIN:type: range,rows: 356.LIKE '%ron'supplies no leading characters → no seek is possible. The planner cannot pick a starting page. It falls back to a full table scan. WorkedEXPLAIN:type: ALL,rows: 493,889.
On Aaron Francis's 491,583-row table, the difference is ~1,387× more estimated rows examined — the difference between a ~millisecond-class lookup and a whole-table scan.
type: range vs type: ALL — the EXPLAIN signature¶
EXPLAIN SELECT * FROM people WHERE first_name LIKE 'Aa%';
-- type: range, key: first_name, rows: 356, Extra: Using index condition
EXPLAIN SELECT * FROM people WHERE first_name LIKE '%ron';
-- type: ALL, key: NULL, rows: 493889, Extra: Using where
When troubleshooting a slow LIKE query, the
EXPLAIN access type is the
first signal: range means you're using the index up to the
wildcard; ALL means you've paid for a table scan.
The non-sargable vocabulary¶
Non-sargable (coined from "search argumentable")
is the general term for any predicate the planner cannot
translate into a direct B+tree seek. Wildcard-prefix
LIKE is one class of non-sargable predicate; the other
canonical class is
index obfuscation
(wrapping an indexed column in a function). Both produce
the same type: ALL EXPLAIN signature for the same
structural reason: the B+tree is not keyed on what the
predicate is asking about.
When suffix / substring search is required¶
For robust suffix or substring search at scale, the MySQL toolbox offers:
- Fulltext indexes
— inverted-index structure over word tokens; answer
MATCH(name) AGAINST ('ron')queries in sub-linear time on token membership, not B+tree prefix. - Reversed-column index — store
REVERSE(name)in a generated column and index it; suffix search becomes prefix search on the reversed column. "LIKE '%ron'" → "LIKE 'nor%'onreverse_name". - External search engine — Elasticsearch, OpenSearch, Typesense for full-text + substring + fuzzy search patterns the RDBMS doesn't model natively.
- Trigram indexes — PostgreSQL's
pg_trgmextension indexes 3-character substrings; MySQL has no native equivalent but the functional-index-on-generated-column pattern can approximate.
Francis notes this is beyond his post's scope but points at PlanetScale's indexing for wildcard searches and fulltext indexes courses.
Seen in¶
- sources/2026-04-21-planetscale-why-isnt-mysql-using-my-index
— canonical framing: "MySQL can use the index until it
reaches the first wildcard character" + worked 491,583-
row
LIKE 'Aa%'vsLIKE '%ron'EXPLAINpair showingtype: range/ 356 rows vstype: ALL/ 493,889 rows.
Related¶
- concepts/secondary-index — the index family that can't serve wildcard-prefix queries.
- concepts/b-plus-tree — the structural reason.
- concepts/index-obfuscation — sibling non-sargable class (function-wrapped indexed column).
- concepts/mysql-explain — the
type: ALLdiagnostic. - concepts/mysql-access-type —
rangevsALLin the access-type ladder.