skip to content

How do you make LIKE match a literal % or _ character in SQL?

level: middleimportance: should knowfreq 55%

answer

  1. the wildcard has to lose its power
  2. an optional clause on the predicate itself
  3. you nominate a character, then prefix it
  4. standard SQL supplies no default for you

basics

~20 s

Nominate an escape character with the ESCAPE clause and put it in front of the wildcard you want taken literally, as in code LIKE '%!_%' ESCAPE '!'. Standard SQL defines no default escape character, so always declare one.

solid answer

~50 s

Use the predicate's `ESCAPE` clause. You nominate a single character, and any `%`, `_` or copy of that character preceded by it is treated as literal text: `WHERE code LIKE '100!% off' ESCAPE '!'` finds the exact string `100% off`. To match the escape character itself, double it (`'!!'` with `ESCAPE '!'`). Standard SQL has **no** default escape character — without the clause, `%` and `_` are always wildcards. Engines differ on defaults: PostgreSQL and MySQL treat a backslash as the escape character even without the clause, while SQL Server and SQLite have no default at all. Backslash is also a poor choice because some engines process it inside string literals too, so pick a character that cannot appear in your data — `!` or `#` — and declare it explicitly. That is the portable answer.

code

sql · 7 lines
sql
-- Over-matching: the underscore is a wildcard
SELECT code FROM products
WHERE  code LIKE 'AB_%';        -- matches 'ABC1', 'ABX9', 'AB_1'

-- Fixed: the underscore is literal, the trailing % still a wildcard
SELECT code FROM products
WHERE  code LIKE 'AB!_%' ESCAPE '!';  -- matches 'AB_1' only

go deeper

for a junior

Know that % and _ can be searched for literally, and that the ESCAPE clause is what makes it happen. Be able to write one pattern that finds a literal underscore.

for a middle

Explain the mechanics: which characters the escape character may precede, how to match the escape character itself by doubling it, and that standard SQL defines no default escape character.

for a senior

Show that you handle patterns built from data you did not write: escape the escape character first, then the wildcards, and choose an escape character that cannot appear in the searched values.

for a principal

Own the convention. Pattern-building duplicated across services drifts, especially between engines with different escape defaults; decide on one shared helper and one declared escape character, and make over-matching visible in tests.

## The problem Inside a `LIKE` pattern, `%` and `_` are metacharacters. If the text you are actually looking for contains one of them, a naive pattern silently over-matches. `WHERE discount_label LIKE '%50%%'` does not mean "contains 50%" — the trailing `%` is just another wildcard, so it means "contains 50", and `'50 units'` comes back too. The worst case is `_`: `WHERE code LIKE '%_%'` matches every value with at least one character, which is usually the entire table. ## The ESCAPE clause The standard predicate is: ```sql <value> [NOT] LIKE <pattern> [ESCAPE <single character>] ``` The `ESCAPE` clause nominates one character. Inside the pattern, that character strips the special meaning from the character that follows it: ```sql SELECT * FROM products WHERE code LIKE 'AB!_%' ESCAPE '!'; -- matches 'AB_1', 'AB_XYZ'; does NOT match 'ABC1' ``` The escape character is followed by `%`, `_`, or itself. To search for the escape character literally, write it twice: with `ESCAPE '!'`, the pattern `'!!'` means a single literal `!`. The standard treats an escape character followed by anything else as an error; engines vary in how strictly they enforce that, so do not rely on other combinations. ## There is no default in standard SQL This is the fact interviewers probe. In standard SQL, if you omit `ESCAPE`, **no** character is an escape character — `%` and `_` are unconditionally wildcards. Engines diverge here: - **PostgreSQL** and **MySQL** treat a backslash as the escape character for `LIKE` even when the clause is absent. - **SQL Server** and **SQLite** have no default; you must write `ESCAPE` (T-SQL additionally offers bracket character classes such as `[%]`, but that is a dialect extension, not the portable answer). So the same pattern can behave differently on two engines. Writing `ESCAPE` explicitly removes the ambiguity, and it costs you one clause. ## Why backslash is a bad choice Backslash is the intuitive pick and the wrong one. In MySQL, backslash is *also* an escape character inside string literals by default, so the literal you type is processed twice and you end up doubling backslashes to get one through. Choosing a character that has no other meaning — `!`, `#`, `|` — and declaring it with `ESCAPE` sidesteps the whole layering problem. Pick one that cannot occur in the searched data, or escape it too. ## Escaping values that come from elsewhere When the pattern is assembled from a value you did not write — a search box, an imported list, a stored prefix — you must escape that value's own `%`, `_` and escape characters before concatenating it into the pattern. Order matters: escape the escape character **first**, then the wildcards, otherwise you re-escape the escape characters you just inserted and corrupt the pattern. Note this is a *correctness* concern, entirely separate from how you bind values into the statement. ## Worked comparison ```sql -- Wrong: the user's underscore is a wildcard WHERE code LIKE '%A_1%'; -- matches 'AB1', 'AX1', ... -- Right: the underscore is literal WHERE code LIKE '%A!_1%' ESCAPE '!'; -- matches 'A_1' only ``` A useful sanity check when debugging an over-matching filter: count the rows returned by the pattern with all wildcards removed. If the filtered count is suspiciously close to the table count, an unescaped `_` or `%` from the search text is usually the reason. ## Interaction with NOT LIKE `ESCAPE` attaches to the predicate, not to the pattern literal, and it works identically under negation: `WHERE code NOT LIKE '%!_%' ESCAPE '!'` returns the rows whose code contains no literal underscore. ## What an interviewer is listening for Three things: that you reach for `ESCAPE` rather than trying to "quote" the wildcard some other way; that you know standard SQL has no default escape character and engine defaults differ; and that you double the escape character to match it literally. A candidate who says "just use a backslash, it always works" has not been bitten by an engine where it does not.

  • With ESCAPE '!', how do you match a value containing a literal exclamation mark?
    Double it: the pattern `'%!!%' ESCAPE '!'` matches any value containing a single literal `!`. The escape character is allowed to escape `%`, `_` or itself, and doubling is how you get it through as data. This is also why you should pick an escape character that does not otherwise occur in the searched text — otherwise every occurrence needs doubling.
  • Why is WHERE note LIKE '%100%%' a bug when you meant to find the text 100%?
    The trailing `%` is read as a wildcard, so the pattern reduces to "contains 100" and returns `'100 units'` as well. You need `LIKE '%100!%%' ESCAPE '!'`, where `!%` is a literal percent sign and the final `%` is still a wildcard. It is a silent bug: the query runs and returns a superset, so nobody notices until a report is wrong.

saying these in an interview costs you the question

  • Assuming a backslash always escapes wildcards without an ESCAPE clause
  • Trying to escape a wildcard by doubling it, as in '%%'
  • Thinking ESCAPE changes the quoting of the string literal
  • Escaping the wildcards before the escape character when building a pattern
  • Believing a bound parameter stops % and _ acting as wildcards

context