SQL LIKE: Pattern Matching With % and _
LIKE matches text against a pattern instead of an exact value. In the pattern, % stands for any number of characters and _ for exactly one. That gives you "starts with", "ends with" and "contains" searches.
What Does LIKE Do?
= only finds values that are exactly equal. LIKE finds values that fit a pattern. Every Gmail user:
SELECT first_name, last_name, email
FROM users
WHERE email LIKE '%@gmail.com'
ORDER BY last_name
LIMIT 5;
▶ Run this query in the SQL Playground
| first_name | last_name | |
|---|---|---|
| Donna | Adams | donna.adams52@gmail.com |
| Susan | Anderson | susan.anderson87@gmail.com |
| Brian | Anderson | brian.anderson56@gmail.com |
| Thomas | Baker | thomas.baker10@gmail.com |
| Sarah | Brown | sarah.brown74@gmail.com |
WHERE column LIKE 'pattern'
WHERE column NOT LIKE 'pattern'
| Wildcard | Matches | Example | Matches values like |
|---|---|---|---|
% | Zero or more characters | 'Ja%' | Ja, James, Jason |
_ | Exactly one character | '_ar%' | Mark, Sarah, Karen (any first letter, then "ar") |
Starts With, Ends With, Contains
| Search | Pattern | Meaning |
|---|---|---|
| Starts with | LIKE 'abc%' | abc, then anything |
| Ends with | LIKE '%abc' | Anything, then abc |
| Contains | LIKE '%abc%' | abc anywhere |
| Exact length | LIKE '_____' | Exactly five characters |
| Fixed format | LIKE '+1-___-___-____' | A phone number in that layout |
Starts with
SELECT name, category
FROM products
WHERE name LIKE 'Pro%'
ORDER BY name;
▶ Run this query in the SQL Playground
| name | category |
|---|---|
| Professional Car Wax Kit | Automotive |
Ends with
SELECT DISTINCT last_name
FROM users
WHERE last_name LIKE '%son'
ORDER BY last_name;
▶ Run this query in the SQL Playground
| last_name |
|---|
| Anderson |
| Jackson |
| Johnson |
| Nelson |
| Robinson |
| Thompson |
| Wilson |
Contains
SELECT DISTINCT name
FROM products
WHERE name LIKE '%Set%'
ORDER BY name;
▶ Run this query in the SQL Playground
| name |
|---|
| LEGO Creator Set 742pcs |
| Luxury Shower Gel Gift Set |
| Resistance Bands Set |
| Soy Wax Candle Set |
| Succulent Plant Set |
Matching One Character With _
_ stands for exactly one character, so it fixes the position of what follows. First names whose second and third letters are "ar":
SELECT DISTINCT first_name
FROM users
WHERE first_name LIKE '_ar%'
ORDER BY first_name;
▶ Run this query in the SQL Playground
| first_name |
|---|
| Barbara |
| Carol |
| Karen |
| Maria |
| Mark |
| Sarah |
It is also handy for codes with a fixed layout. Every Playground SKU is SKU- plus six digits, so this finds SKU-000010 to SKU-000019:
SELECT sku, name
FROM products
WHERE sku LIKE 'SKU-00001_'
ORDER BY sku;
▶ Run this query in the SQL Playground
| sku | name |
|---|---|
| SKU-000010 | Magnetic Phone Mount Pro |
| SKU-000011 | Blender Pro 1200W |
| SKU-000012 | Ergonomic Desk Mat XL |
| SKU-000013 | Retinol Eye Cream 30ml |
| SKU-000014 | Play Kitchen Deluxe |
| SKU-000015 | Japanese Green Tea 50 bags |
| SKU-000016 | Sticky Notes Assorted 18pk |
| SKU-000017 | A5 Hardcover Notebook 3pk |
| SKU-000018 | Winter Parka Jacket |
| SKU-000019 | Resistance Bands Set |
And a format check: every user's phone number fits the pattern +1-___-___-____:
SELECT COUNT(*) AS users,
SUM(phone LIKE '+1-___-___-____') AS phones_in_format
FROM users;
▶ Run this query in the SQL Playground
| users | phones_in_format |
|---|---|
| 150 | 150 |
NOT LIKE
NOT LIKE keeps the rows that do not match. Users on email providers other than Gmail, Yahoo and Hotmail:
SELECT COUNT(*) AS other_providers
FROM users
WHERE email NOT LIKE '%@gmail.com'
AND email NOT LIKE '%@yahoo.com'
AND email NOT LIKE '%@hotmail.com';
▶ Run this query in the SQL Playground
| other_providers |
|---|
| 62 |
Each pattern needs its own LIKE or NOT LIKE, joined with AND or OR; LIKE does not take a list. Rows where the column is NULL match neither LIKE nor NOT LIKE.
Is LIKE Case-Sensitive?
It depends on the database:
| Database | LIKE | Case-insensitive option |
|---|---|---|
| SQLite (Playground) | Case-insensitive for A-Z | GLOB is the case-sensitive alternative |
| MySQL | Case-insensitive with the default collations | Use a binary or _cs collation for case-sensitive |
| PostgreSQL | Case-sensitive | ILIKE |
| SQL Server | Depends on the collation; usually case-insensitive | COLLATE ... _CS_ ... for case-sensitive |
| Oracle | Case-sensitive | LOWER(col) LIKE 'abc%' |
In the Playground, LIKE ignores case while GLOB (which uses * and ? as wildcards) does not:
SELECT SUM(name LIKE '%set%') AS like_lowercase,
SUM(name LIKE '%SET%') AS like_uppercase,
SUM(name GLOB '*set*') AS glob_lowercase,
SUM(name GLOB '*Set*') AS glob_capital_s
FROM products;
▶ Run this query in the SQL Playground
| like_lowercase | like_uppercase | glob_lowercase | glob_capital_s |
|---|---|---|---|
| 8 | 8 | 0 | 8 |
The portable way to search regardless of case is to lowercase both sides: WHERE LOWER(name) LIKE '%set%'.
Searching for a Literal % or _
To find a real percent sign or underscore, tell LIKE which character escapes the next one. The sample values below are written into the query:
WITH promos(code) AS (
VALUES ('SAVE10%'), ('SAVE10'), ('NEW_USER'), ('NEWUSER')
)
SELECT code,
code LIKE '%!%%' ESCAPE '!' AS has_percent_sign,
code LIKE '%!_%' ESCAPE '!' AS has_underscore,
code LIKE '%_%' AS unescaped_underscore
FROM promos;
▶ Run this query in the SQL Playground
| code | has_percent_sign | has_underscore | unescaped_underscore |
|---|---|---|---|
| SAVE10% | 1 | 0 | 1 |
| SAVE10 | 0 | 0 | 1 |
| NEW_USER | 0 | 1 | 1 |
| NEWUSER | 0 | 0 | 1 |
Unescaped, '%_%' means "at least one character", so it matches every code. With ! as the escape character, !_ means a real underscore. Any character not used in the data can be the escape character.
LIKE on Numbers and Dates
LIKE works on text. Dates in the Playground are text in YYYY-MM-DD form, so a prefix pattern finds a month:
SELECT COUNT(*) AS orders_in_dec_2024
FROM orders
WHERE order_date LIKE '2024-12-%';
▶ Run this query in the SQL Playground
| orders_in_dec_2024 |
|---|
| 6 |
It works, but a range (order_date >= '2024-12-01' AND order_date < '2025-01-01') is the better habit: it works for real date types in every database and can use an index. For numbers, compare numerically with BETWEEN or >; CAST(price AS TEXT) LIKE '1%' runs but is rarely what you mean.
LIKE and Indexes
| Pattern | Can use a normal index? | Why |
|---|---|---|
LIKE 'Pro%' | Often yes | A fixed prefix is a range in the sorted index (SQLite needs case-insensitive matching or a matching collation) |
LIKE '%son' | No | Any value could end with "son", so every row is checked |
LIKE '%Set%' | No | Same: a leading wildcard |
LOWER(name) LIKE 'pro%' | Only with an expression index on LOWER(name) | The function hides the column |
For "contains" searches over large text, databases offer full-text search: FTS5 in SQLite, tsvector in PostgreSQL, FULLTEXT indexes in MySQL and SQL Server.
Common LIKE Mistakes
Five errors and how to avoid them.
1. Using = with wildcards
SELECT * FROM users
WHERE last_name = 'A%';
SELECT * FROM users
WHERE last_name LIKE 'A%';
With =, the % is just a character, so only a name that is literally "A%" would match.
2. Forgetting a % in a contains search
WHERE name LIKE 'Set%'
WHERE name LIKE '%Set%'
Each side of the word needs its own % to allow text before and after it.
3. Assuming LIKE ignores case everywhere
WHERE name LIKE '%set%' -- 0 rows in PostgreSQL
WHERE LOWER(name) LIKE '%set%'
SQLite and MySQL ignore case by default; PostgreSQL and Oracle do not.
4. One LIKE with several values
WHERE email LIKE ('%@gmail.com', '%@yahoo.com')
WHERE email LIKE '%@gmail.com'
OR email LIKE '%@yahoo.com'
LIKE compares with one pattern. Combine several with OR.
5. Leading wildcards on a big table
WHERE name LIKE '%phone%' -- millions of rows
WHERE name LIKE 'phone%'
-- or a full-text index for word searches
A leading % rules out an index range. On large tables, design the search so it can use an index.
LIKE Cheat Sheet
| Task | Pattern |
|---|---|
| Starts with abc | LIKE 'abc%' |
| Ends with abc | LIKE '%abc' |
| Contains abc | LIKE '%abc%' |
| Second letter is a | LIKE '_a%' |
| Exactly 4 characters | LIKE '____' |
| Does not contain abc | NOT LIKE '%abc%' |
| Literal % | LIKE '%!%%' ESCAPE '!' |
| Ignore case anywhere | LOWER(col) LIKE '%abc%' (PostgreSQL: ILIKE) |
Practice LIKE Queries
5 exercises on the real Playground tables.
Write each query yourself first. Every solution has been run against the Playground database.
-
1Easy List the products whose name ends with "Kit". Table:
productsShow solution
SELECT name, category FROM products WHERE name LIKE '%Kit' ORDER BY name;▶ Run this query in the SQL Playground
The % goes before the fixed ending.
-
2Easy Count the employees whose title contains "Engineer". Table:
employeesShow solution
SELECT COUNT(*) AS engineers FROM employees WHERE title LIKE '%Engineer%';▶ Run this query in the SQL Playground
Contains needs % on both sides; it also matches "Engineering".
-
3Medium List the distinct first names that are exactly four letters long. Table:
usersShow solution
SELECT DISTINCT first_name FROM users WHERE first_name LIKE '____' ORDER BY first_name LIMIT 8;▶ Run this query in the SQL Playground
Four underscores mean exactly four characters.
-
4Medium Count the users per email provider for Gmail, Yahoo and everyone else. Table:
usersShow solution
SELECT CASE WHEN email LIKE '%@gmail.com' THEN 'Gmail' WHEN email LIKE '%@yahoo.com' THEN 'Yahoo' ELSE 'Other' END AS provider, COUNT(*) AS users FROM users GROUP BY provider ORDER BY users DESC;▶ Run this query in the SQL Playground
LIKE works inside CASE as well as in WHERE.
-
5Medium Count the reviews whose comment mentions "price" in any capitalisation. Table:
reviewsShow solution
SELECT COUNT(*) AS mentions_price FROM reviews WHERE LOWER(comment) LIKE '%price%';▶ Run this query in the SQL Playground
LOWER makes the search work the same way in every database.
📝 Quiz — LIKE
Test your understanding · 0/6 answered
What does the SQL LIKE operator do?
With the wildcards % and _.
Which condition finds names starting with "Ra"?
'Ra%' means Ra followed by anything.
What does % match?
Even nothing: 'Ra%' matches 'Ra' itself.
What does LIKE '_erry' match?
_ is exactly one character.
Which pattern finds values containing "phone"?
A % on each side allows text before and after.
What does NOT LIKE do?
NULL values match neither LIKE nor NOT LIKE.
LIKE Interview Questions
Q1. What is the difference between % and _?
% matches any number of characters, including none; _ matches exactly one character.
Q2. Is LIKE case-sensitive?
It depends on the database. SQLite and MySQL (default collations) ignore case, PostgreSQL and Oracle do not, and SQL Server depends on the collation. PostgreSQL has ILIKE; LOWER(col) LIKE works everywhere.
Q3. Why is LIKE '%abc' slow on large tables?
A leading wildcard means the value can start with anything, so a sorted index cannot narrow the search and every row is checked. Use a prefix search or full-text search.
Q4. How do you search for a literal underscore?
Escape it: LIKE '%!_%' ESCAPE '!'. Without the escape, _ means any single character.
Q5. How do you match several patterns?
Combine LIKE conditions with OR. PostgreSQL also has LIKE ANY (ARRAY[...]) and SIMILAR TO, and several databases offer regular expressions (REGEXP, ~, REGEXP_LIKE).
Frequently Asked Questions
What does the SQL LIKE operator do?
LIKE searches for a pattern in a text value instead of an exact match. It uses two wildcards: % (any number of characters) and _ (exactly one character), usually in a WHERE clause. Example: WHERE email LIKE '%@gmail.com'.
What is the difference between % and _ in SQL LIKE?
% matches zero or more characters, so 'Ja%' matches Ja, James and Jason. _ matches exactly one character, so '_ar%' matches Mark and Sarah but not Arthur.
Why does 'A%' not work with =?
With =, the percent sign is treated as an ordinary character, so WHERE name = 'A%' only finds a name that is literally A%. Use LIKE to make % a wildcard.
Is SQL LIKE case-sensitive?
It depends on the database. SQLite and MySQL (with default collations) ignore case. PostgreSQL and Oracle are case-sensitive; PostgreSQL offers ILIKE. SQL Server depends on the collation. The portable approach is WHERE LOWER(name) LIKE '%abc%'.
What does NOT LIKE do?
NOT LIKE returns the rows whose value does not match the pattern. WHERE name NOT LIKE 'A%' returns every name that does not start with A. Rows where the column is NULL are returned by neither LIKE nor NOT LIKE.
Can SQL LIKE search numbers?
Only as text: a text column, or a number converted with CAST. For numeric conditions, BETWEEN and comparison operators are clearer and faster.
Can SQL LIKE search dates?
If dates are stored as text in YYYY-MM-DD form, as in the Playground, WHERE order_date LIKE '2024-12-%' works. A date range (>= start AND < end) is better: it works with real date types and can use an index.
Does SQL LIKE use indexes?
Only when the pattern starts with fixed text. LIKE 'Ra%' can use an index; LIKE '%Ra%' cannot and reads every row. For large contains searches, use full-text search.
What is PostgreSQL ILIKE?
ILIKE is PostgreSQL's case-insensitive LIKE: WHERE name ILIKE 'a%' matches Alice, alice and ALICE. It is not available in MySQL, SQL Server or SQLite.
What is the difference between LIKE and ILIKE?
In PostgreSQL, LIKE is case-sensitive and ILIKE is not. In databases where LIKE already ignores case, such as SQLite and MySQL, ILIKE does not exist.
Can SQL LIKE search email addresses?
Yes. WHERE email LIKE '%@gmail.com' finds Gmail addresses, WHERE email LIKE 'john%' finds addresses starting with john, and WHERE email LIKE '%@%.edu' finds .edu addresses.
Can SQL LIKE search phone numbers?
Yes, as text. WHERE phone LIKE '+1-%' finds numbers with that prefix, and WHERE phone LIKE '+1-___-___-____' checks a fixed layout: every phone number in the Playground matches it.
Can SQL LIKE match special characters?
Only % and _ have a special meaning; dashes, dots, @ and spaces match literally. To search for a literal % or _, use ESCAPE, for example WHERE code LIKE '%!%%' ESCAPE '!'.