Practice13problems on this pattern, easy to hardStart practising →
Pattern 1SQL course / Foundations

SQL WHERE: Filtering rows and combining conditions correctly

A query can run without errors and still return the wrong rows. Often, the problem is a small difference between the business requirement and the condition written in SQL.

7 min read8 sections3 predictions13 practice problems

In this pattern, learn to turn a requirement into a filter condition: compare values, combine conditions, match lists and text patterns, and choose the right range boundaries. By the end, you will be able to reason why each row belongs in your result.

01

WHERE Basics

Below is an orders table with some columns. First, look at the data carefully.

orders4 rows
order_idcustomeramount
1Asha120
2Ben80
3Carla200
4Dev60

Now let’s say we need orders with an amount greater than 100. Below is a SQL query for the same:

Query
SELECT order_id, customer, amount
FROM orders
WHERE amount > 100;

WHERE amount > 100 describes which rows match the above condition. Conceptually, SQL checks that condition for each candidate row:

the per-row decision4 rows
customeramountamount > 100Keep the row?
Asha120TRUEYes
Ben80FALSENo
Carla200TRUEYes
Dev60FALSENo

Since the condition matches only Asha’s and Carla’s orders. We got them in the Result.

Result2 rows
order_idcustomeramount
1Asha120
3Carla200
Remember

WHERE keeps a row only when its complete condition evaluates to TRUE.

Tip
SQL can also return UNKNOWN when missing values are involved. WHERE excludes that result too. That is why only TRUE rows remain. P02 explains NULL and UNKNOWN Examples in this pattern focus on known values only.
02

Comparison operators

The table below shows all the comparison operators we use in SQL. A table is provided to understand what a requirement might be and how that translates into comparison operations.

comparison operators6 rows
OperatorMeaningExample
=Equal tostatus = 'paid'
<>Not equal tostatus <> 'cancelled'
>Greater thanamount > 100
>=Greater than or equal toamount >= 100
<Less thanamount < 500
<=Less than or equal toamount <= 500

Note that the text values are used with single quotes, as in 'paid'. However, numeric values don’t require them.

A comparison expresses one decision. In the next section, we will see how to query multiple conditions.

03

Combining conditions with AND and OR

Here is an order status table. Using this table, we will introduce multiple SQL conditions like AND and OR.

order_status5 rows
order_idcountrystatus
1Indiapaid
2Indiacancelled
3UKpending
4Indiapending
5UAErefunded

AND requires every condition to be TRUE

Use AND when you need to check multiple conditions at the same time. A row is included in the result only when all the conditions are satisfied.

RequirementFind paid orders from India.
Query
SELECT order_id, country, status
FROM order_status
WHERE country = 'India'
AND status = 'paid';
Result1 row
order_idcountrystatus
1Indiapaid

OR requires at least one of the conditions to be TRUE

Use OR when a row can match any one of several conditions. Unlike AND, which requires every condition to be true, OR includes a row even if only one condition is satisfied.

RequirementFind all paid or pending orders from any country.
Query
SELECT order_id, country, status
FROM order_status
WHERE status = 'paid'
OR status = 'pending';
Result3 rows
order_idcountrystatus
1Indiapaid
3UKpending
4Indiapending

Now, let’s change the requirement to: Return paid or pending orders from India. Both statuses must be restricted to India.

We have written a query for this requirement. Now, can you predict what this should return?

Pause and predictPredict before you continue

Which orders does it actually return?

The query
SELECT order_id, country, status
FROM order_status
WHERE country = 'India'
AND status = 'paid'
OR status = 'pending';
Remember

Always use parentheses when multiple operations are involved to clarify what the query should do.

04

IN and NOT IN: To match a list of values

When we have a list of values to match in a given column, IN makes the filter condition easier to read.

So, instead of:

Query
WHERE status = 'paid'
OR status = 'pending'
OR status = 'processing'

we write using,

Query
WHERE status IN ('paid', 'pending', 'processing')

Similarly, we can use NOT IN to exclude the listed values.

Query
WHERE status NOT IN ('cancelled', 'refunded');
05

Matching text patterns with LIKE

In SQL, the LIKE operator is used to search for a specific pattern in a string column. It is commonly used when you want to find records that start with, end with, or contain certain characters.

We will use the Customers table to explain the concepts here,

customers6 rows
customer_idcustomer
1Asha
2Amit
3Anand
4Bhavna
5Sam
6Ali

SQL LIKE uses two main wildcard characters:

wildcards2 rows
WildcardMeaningExample
%Matches zero or more characters'home%'
_Matches exactly one character'home_'

Use Case:

1. Find names beginning with A:

Query
SELECT customer_id, customer
FROM customers
WHERE customer LIKE 'A%';
Result4 rows
customer_idcustomer
1Asha
2Amit
3Anand
6Ali

The A is fixed; % allows any remaining characters. It can also match zero characters, so the name A would qualify if it existed.

Where you place % determines which values match.

Query
LIKE 'an%' -- begins with an
LIKE '%an' -- ends with an
LIKE '%an%' -- contains an anywhere

2. Match exactly one character with _

Query
SELECT customer_id, customer
FROM customers
WHERE customer LIKE '_m%';

_ allows any first character, m fixes the second character, and % allows the rest. The result is Amit, customer 2.

Similarly, we can use LIKE with a NOT operator to exclude a pattern.

Pause and predictPredict before you continue

Which customers does this query return?

The query
SELECT customer_id, customer
FROM customers
WHERE customer NOT LIKE 'A%';
Tip
Escaping wildcards in LIKE
  • In SQL, _ matches exactly one character, while % matches zero or more characters.
  • LIKE 'SAVE_10' matches SAVE_10, SAVEA10, and SAVEB10 because _ is a wildcard.
  • To match a literal underscore (_), use ESCAPE:
Query
WHERE code LIKE 'SAVE!_10' ESCAPE '!';
  • Here, !_ matches a literal _. Similarly, !% matches a literal %.

Pause and predict

Pause and predictPredict before you continue

Which condition finds names whose second character is m?

The query
-- Option A
WHERE customer LIKE 'm%'
-- Option B
WHERE customer LIKE '_m%'
-- Option C
WHERE customer LIKE '%_m'
06

Match ranges with BETWEEN

The SQL BETWEEN operator filters values within a specified range, including both boundary values.

Syntax
WHERE column_name BETWEEN lower_value AND upper_value

Let’s consider the ORDERS table

orders5 rows
order_idamount
180
2100
3150
4300
5450
Query
SELECT order_id, amount
FROM orders
WHERE amount BETWEEN 100 AND 300;
Result3 rows
order_idamount
2100
3150
4300

BETWEEN includes both endpoints. The condition means:

Query
WHERE amount >= 100
AND amount <= 300
Tip
Boundary order in BETWEEN
  • Always specify the lower boundary first, followed by the upper boundary.
  • BETWEEN 300 AND 100 checks for values >= 300 AND <= 100, so no numeric value can match.
  • Use BETWEEN 100 AND 300 instead.

Timestamp ranges

We will now use a timestamp filter. Lets see the Orders table below:

orders4 rows
order_idcreated_at
12026-07-01 09:00:00
22026-07-31 00:00:00
32026-07-31 18:30:00
42026-08-01 00:00:00
RequirementFrom the ORDERS table above, return every July order.

Here, if we use the condition created_at BETWEEN '2026-07-01' AND '2026-07-31', then it can miss Order Id 3. Because it will check BETWEEN 2026-07-01 09:00:00 and 2026-07-31 00:00:00.

To ensure we don’t miss any probable Ids, Use the start of the next month as the upper boundary:

Query
SELECT order_id, created_at
FROM orders
WHERE created_at >= '2026-07-01'
AND created_at < '2026-08-01';
07

Putting all the requirements together

Let’s solve a problem that takes into consideration all the above concepts.

RequirementReturn Indian orders placed in July that are either paid or pending and have an amount between 100 and 500.
orders6 rows
order_idcustomercountrystatusamountcreated_at
1AshaIndiapaid1002026-07-03 10:15:00
2BenUKpending2002026-07-08 14:00:00
3CarlaIndiapaid3502026-08-01 00:00:00
4DevIndiacancelled2502026-07-12 11:30:00
5EshaIndiapending802026-07-20 16:45:00
6FarahIndiapending5002026-07-31 18:30:00

Solution:

First split the requirement one by one as follows:

  1. Country must be India.
  2. Order time must fall within July.
  3. Status must be paid or pending.
  4. Amount must be from 100 through 500, inclusive.

Then combine these into where clause and display the result as shown below:

Query
SELECT
order_id,
customer,
country,
status,
amount,
created_at
FROM orders
WHERE country = 'India'
AND created_at >= '2026-07-01'
AND created_at < '2026-08-01'
AND status IN ('paid', 'pending')
AND amount BETWEEN 100 AND 500;
Result2 rows
order_idcustomercountrystatusamountcreated_at
1AshaIndiapaid1002026-07-03 10:15:00
6FarahIndiapending5002026-07-31 18:30:00
08

Summary

In this section, we learned how to use the WHERE clause to filter rows using operators such as AND, OR, IN, NOT IN, LIKE, and BETWEEN.

We also explored how to combine conditions and avoid common filtering mistakes. With these fundamentals in place, we’re ready to move on to Pattern P02.

Practise this patternBrowse filtering practice →

Graded problems on this pattern, easy to hard.

Up next · Pattern 2Working with NULL

In P01, we learned how to filter rows using SQL conditions. In P02, we’ll explore how NULL affects comparisons, why some conditions produce unexpected results, and how to avoid common pitfalls such as the NOT IN subquery trap.

Go deeper

Ask Colearn about this pattern

Use your personal learning companion to explain your own filter or test your reasoning. Ask about the concept, or paste a simplified version of your query.

Instant answers, grounded in the same verified material the diagnostic grades against.

3 of 3 free questions left
Why does this filter include a pending order from the wrong country?

AND groups before OR, so “country = 'India' AND status = 'paid' OR status = 'pending'” is read as “(India AND paid) OR pending”. A pending order from any country satisfies the second branch.

Put the alternatives in parentheses so the country applies to both: country = 'India' AND (status = 'paid' OR status = 'pending'), or write status IN ('paid', 'pending').

What is the difference between IN ('Ind%') and LIKE 'Ind%'?

IN compares exact values, so IN ('Ind%') looks for the literal text “Ind%”. The % only acts as a wildcard with LIKE.

LIKE 'Ind%' means “starts with Ind”: % matches zero or more characters and _ matches exactly one.

Why does my timestamp filter miss the last day?

On a timestamp column, BETWEEN a start date AND an end date stops at midnight at the start of the end date — so everything that happened later on that last day is excluded.

Use a half-open range instead: greater than or equal to the start, and strictly less than the start of the next period. It includes the whole last day and never double-counts a boundary.

Which boundary rows should I test for this requirement?

First try rows that fail one requirement at a time — wrong country, wrong month, wrong status, amount too low — and confirm each one is excluded.

Then test the exact edges: the first moment of the period, the start of the next period, and both ends of any BETWEEN range. BETWEEN includes both ends; a half-open range excludes the next start.

Don’t paste credentials, personal data, or confidential production records.