Pattern Matching: LIKE, IN, BETWEEN
Progress Tracking
Log in to save this lesson and continue from where you left off.
Beyond Exact Matches
So far, your WHERE clauses check for exact matches: department = 'HR', salary = 90000. But real questions are often fuzzier:
- Find products that start with 'Sh'
- Get orders from any of these three months
- Show salaries between 50,000 and 100,000
That's what LIKE, IN, and BETWEEN are for. They give you flexible ways to match patterns and ranges.
LIKE: Pattern Matching for Text
LIKE lets you search for patterns in text using two wildcard characters:
%— matches any sequence of characters (including none)_— matches exactly one character
Pattern Matching with LIKE
SELECT *
FROM orders
WHERE order_details LIKE 'Sh%';| id | first_name | last_name | department | salary | phone_number | joining_date |
|---|---|---|---|---|---|---|
| 1 | Sarah | Mitchell | HR | 95000 | 555-0101 | 2021-03-15 |
| 2 | Michael | Chen | HR | 88000 | 555-0102 | 2022-06-01 |
| 3 | Emily | Rodriguez | HR | 82500 | 2021-09-20 | |
| 4 | David | Park | HR | 80000 | 555-0104 | 2023-01-10 |
| 5 | Lisa | Thompson | HR | 65000 | 2021-04-05 |
Find employees whose first name starts with S from `techcorp_workforce`.
The % after 'Sh' means "Sh followed by anything." So Shoes, Shirts, and Shorts would all match.
Ends With
| id | first_name | last_name | department | salary | phone_number | joining_date |
|---|---|---|---|---|---|---|
| 1 | Sarah | Mitchell | HR | 95000 | 555-0101 | 2021-03-15 |
| 2 | Michael | Chen | HR | 88000 | 555-0102 | 2022-06-01 |
| 3 | Emily | Rodriguez | HR | 82500 | 2021-09-20 | |
| 4 | David | Park | HR | 80000 | 555-0104 | 2023-01-10 |
| 5 | Lisa | Thompson | HR | 65000 | 2021-04-05 |
SELECT
first_name,
last_name
FROM techcorp_workforce
WHERE last_name LIKE '%a';SELECT
first_name,
last_name,
department
FROM techcorp_workforce
WHERE first_name LIKE '%i%';Single Character Matching with _
The underscore matches exactly one character. This is useful when you know the pattern length.
-- Find 3-letter product names
WHERE order_details LIKE '___'
-- Find names starting with any letter, then 'oe'
WHERE first_name LIKE '_oe'Underscores are less common than %, but handy for fixed-length codes or IDs.
Practice: Underscore Wildcards
| worker_id | first_name | last_name | salary | joining_date | department |
|---|---|---|---|---|---|
| 1 | Monika | Arora | 100000 | 2014-02-20 | HR |
| 2 | Niharika | Verma | 80000 | 2014-06-11 | Admin |
| 3 | Vishal | Singhal | 300000 | 2014-02-20 | HR |
| 4 | Amitah | Singh | 500000 | 2014-02-20 | Admin |
| 5 | Vivek | Bhati | 500000 | 2014-06-11 | Admin |
Find all workers whose first name contains 6 letters and also ends with the letter 'h'. Display all information about the workers in output.
Case Sensitivity
In PostgreSQL, LIKE is case-sensitive. 'Sh%' won't match 'shoes' or 'SHIRTS'.
If you need case-insensitive matching, use ILIKE (PostgreSQL only) or LOWER():
-- PostgreSQL: ILIKE is case-insensitive
WHERE order_details ILIKE 'sh%'
-- Works in all dialects: convert to lowercase first
WHERE LOWER(order_details) LIKE 'sh%'Practice: LIKE
| filename | contents |
|---|---|
| draft1.txt | The stock exchange predicts a bull market which would make many investors happy. |
| draft2.txt | The stock exchange predicts a bull market which would make many investors happy, but analysts warn of possibility of too much optimism and that in fact we are awaiting a bear market. |
| final.txt | The stock exchange predicts a bull market which would make many investors happy, but analysts warn of possibility of too much optimism and that in fact we are awaiting a bear market. As always predicting the future market is an uncertain game and all investors should follow their instincts and best practices. |
Find drafts which contains the word 'optimism'.
IN: Matching a List of Values
Remember chaining ORs to check multiple values? IN does the same thing, but cleaner.
Instead of:
WHERE department = 'HR'
OR department = 'Admin'
OR department = 'Engineering'Write:
WHERE department IN ('HR', 'Admin', 'Engineering')Much better. Let's see it in action:
Find all employees in the HR, Admin, or Account departments using IN.
This returns all HR, Admin, and Account employees.
IN with Numbers
IN works with any data type, not just text:
SELECT
id,
cust_id,
order_details
FROM orders
WHERE cust_id IN (3, 7, 15);This gets all orders from customers with IDs 3, 7, or 15.
NOT IN: Excluding Values
Add NOT to exclude values from the list:
SELECT
first_name,
last_name,
department
FROM techcorp_workforce
WHERE department NOT IN ('Admin');This returns everyone except Admin employees.
IN is cleaner than OR chains and easier to modify. Use it whenever you're checking a column against multiple specific values.
Watch out for NOT IN with NULL values. If your list contains any NULLs, the entire NOT IN check returns no rows. This happens because comparisons with NULL return NULL (unknown), not true or false. We'll explain this fully in Lesson 1.6.
Practice: IN
| id | name | sex | age | height | weight | team | noc | games | year | season | city | sport | event | medal |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 3520 | Guillermo J. Amparan | M | Mexico | MEX | 1924 Summer | 1924 | Summer | Paris | Athletics | Athletics Men's 800 metres | ||||
| 35394 | Henry John Finchett | M | Great Britain | GBR | 1924 Summer | 1924 | Summer | Paris | Gymnastics | Gymnastics Men's Rings | ||||
| 21918 | Georg Frederik Ahrensborg Clausen | M | 28 | Denmark | DEN | 1924 Summer | 1924 | Summer | Paris | Cycling | Cycling Men's Road Race Individual | |||
| 110345 | Marinus Cornelis Dick Sigmond | M | 26 | Netherlands | NED | 1924 Summer | 1924 | Summer | Paris | Football | Football Men's Football | |||
| 54193 | Thodore Tho Jeitz | M | 26 | Luxembourg | LUX | 1924 Summer | 1924 | Summer | Paris | Gymnastics | Gymnastics Men's Individual All-Around |
Find all athletes who were older than 40 years when they won either Bronze or Silver medals.
BETWEEN: Range Filtering
BETWEEN filters for values within a range, including both endpoints.
Find employees with a salary between 70000 and 100000 (inclusive).
BETWEEN includes both endpoints. The query above includes employees earning exactly 70,000 and exactly 100,000.
BETWEEN is Shorthand
BETWEEN is equivalent to using >= and <= with AND:
-- These are identical:
WHERE salary BETWEEN 70000 AND 100000
WHERE salary >= 70000
AND salary <= 100000BETWEEN is just cleaner to read.
BETWEEN with Dates
BETWEEN is especially useful for date ranges:
SELECT
first_name,
last_name,
joining_date
FROM techcorp_workforce
WHERE joining_date BETWEEN '2022-01-01' AND '2022-12-31';This gets all employees who joined in 2022. Now try it with orders — get all orders from March 2019.
Keep in mind that Oracle doesn't implicitly cast string literals to dates — use a DATE literal or TO_DATE(). The other three dialects accept the plain string format directly.
| id | cust_id | order_date | order_details | total_order_cost |
|---|---|---|---|---|
| 1 | 3 | 2019-03-04 | Coat | 100 |
| 2 | 3 | 2019-03-01 | Shoes | 80 |
| 3 | 3 | 2019-03-07 | Skirt | 30 |
| 4 | 7 | 2019-02-01 | Coat | 25 |
| 5 | 7 | 2019-03-10 | Shoes | 80 |
Find all orders placed during March 2019.
If your date column includes time (timestamps), BETWEEN '2019-03-01' AND '2019-03-31' might miss records from March 31st after midnight. For timestamps, it's safer to use: WHERE date_col >= '2019-03-01' AND date_col < '2019-04-01'. We'll cover this in Module 5.
NOT BETWEEN
Exclude a range with NOT BETWEEN:
SELECT
first_name,
last_name,
salary
FROM techcorp_workforce
WHERE salary NOT BETWEEN 70000 AND 100000;This returns employees earning less than 70,000 or more than 100,000.
Combine BETWEEN with other conditions.
| index | weather | hour | travel_distance | gasoline_cost |
|---|---|---|---|---|
| 0 | cloudy | 7 | 24.47 | 1.13 |
| 1 | cloudy | 23 | 23.67 | 1.99 |
| 2 | sunny | 17 | 20.93 | 0.86 |
| 3 | rainy | 2 | 29.58 | 0.85 |
| 4 | rainy | 7 | 16.11 | 0.95 |
Find all Lyft rides which happened on rainy days before noon.
Combining LIKE, IN, and BETWEEN
These operators work with AND and OR just like any other condition:
SELECT
first_name,
last_name,
department,
salary
FROM techcorp_workforce
WHERE department IN ('HR', 'Engineering')
AND salary BETWEEN 80000 AND 200000
AND first_name LIKE 'V%';This finds HR or Engineering employees, earning 80k to 200k, whose first name starts with V.
Try It
| id | cust_id | order_date | order_details | total_order_cost |
|---|---|---|---|---|
| 1 | 3 | 2019-03-04 | Coat | 100 |
| 2 | 3 | 2019-03-01 | Shoes | 80 |
| 3 | 3 | 2019-03-07 | Skirt | 30 |
| 4 | 7 | 2019-02-01 | Coat | 25 |
| 5 | 7 | 2019-03-10 | Shoes | 80 |
Find orders where the product name starts with 'S' and the total cost is between 50 and 100.
Key Takeaways
LIKEuses%(any characters) and_(single character) for pattern matching.INchecks if a value matches any item in a list.BETWEENfilters for values within a range (inclusive on both ends).- All three can be negated with
NOT. - These operators combine with
AND/ORlike any other condition.
What's Next
There's one more filtering concept to cover: NULL values. They behave differently than you might expect, and handling them correctly is essential. That's the focus of the next lesson.