learn-sql-with-phoebe / Builder session 2 of 10
Learn SQL with Phoebe · Builder track · Session 2 of 10

Filtering rows with WHERE

Session 1 handed you every row in a table. Real questions almost never want every row - they want the Pro customers, the coffees under $18, the orders from Canada. WHERE is the clause that asks for SOME rows instead of all of them, and it is the single most-used word in day-to-day SQL. Same Daybreak database, same live editor - now you get to be picky.

🟢 Builder track Practitioners: analysts · DE · DS · PMs Runs in your browser · SQLite ~45 min
0-3 · Welcome 3-20 · WHERE & operators 20-42 · Build-along: filter Daybreak 42-45 · Q&A
Part 0

Where WHERE fits

In session b1 you ran SELECT ... FROM ... and got the whole table back. That is fine for eight products, hopeless for a million orders. WHERE sits right after FROM and keeps only the rows that pass a test you write - "price under 18", "country is Canada", "plan is Pro". Everything you already know still works; you are just adding one line that does the filtering.

Live - presented in session Self-study - read after class ▶ Live SQL - editable & runnable Official sources covered
★ What you walk out with today A working WHERE reflex - comparison operators, AND/OR/NOT to combine tests, and the pattern-and-set family (LIKE, IN, BETWEEN, IS NULL). By the end you can carve any precise slice of Daybreak you want and hand it over.
Part 1 · covers Mode "Basic - WHERE, comparison operators", SQLBolt L2, W3Schools "Where"

WHERE and the comparison operators 7 min live

A WHERE clause is a test applied to every row, one at a time. If the test is true, the row comes back; if false, it is dropped. The tests are built from comparison operators you already know from a calculator: = equals, != not equal, > greater, < less, >= and <=. Text goes in single quotes; numbers do not.

all 8 products WHERE category = 'Coffee' 4 coffees Every row is tested once. True rows pass through; false rows are dropped on the floor. SELECT name, category, price FROM products WHERE category = 'Coffee'; Text values need single quotes: 'Coffee'. Numbers do not: price < 18. A common first slip.
🔍 Click to zoom - WHERE is a funnel that keeps only the rows that pass the test
LiveFilter by an exact match3 min

Here is the funnel from the diagram, live. This asks for only the rows where category equals 'Coffee'. Press ▶ Run, then change 'Coffee' to 'Equipment' or 'Add-on' and run again - watch which rows survive.

SELECT name, category, price
FROM products
WHERE category = 'Coffee';
Quotes matter 'Coffee' is text, so it needs single quotes. Miss them and SQLite thinks Coffee is a column name and errors. Numbers like 18 are bare, no quotes.
LiveFilter by a number range3 min

Comparison operators shine on numbers. This keeps only products priced below 18. Change < to >=, or the 18 to 25, and re-run to feel how the operator picks the rows.

SELECT name, price
FROM products
WHERE price < 18;
Real world

"Show me everything under our free-shipping threshold." That request is a one-line WHERE price < 18. Pricing pages, discount tiers, "budget picks" carousels - all of it starts as a comparison filter on one number column. The clause you just ran is quietly powering shopping sites everywhere.

Part 2 · covers Mode "logical operators", SQLBolt L3, W3Schools "And/Or/Not"

Combining tests: AND, OR, NOT 6 min live

One test is useful; combining them is where filtering gets sharp. AND means a row must pass both tests. OR means it needs pass either. NOT flips a test. String enough together and you can describe almost any slice of customers in one line - "Pro plan, in the USA, signed up this year".

AND keeps only the double-shaded strip; OR keeps both boxes plan = 'Pro' country = 'USA' AND kept AND: plan = 'Pro' and country = 'USA' - the narrow strip, fewer rows. OR (Part 2's other query): country = 'Canada' OR country = 'UK' keeps the union, more rows.
🔍 Click to zoom - AND narrows to the overlap, OR widens to the union
ConnectorRow is kept when...Plain-English read
ANDboth tests are true"Pro and in the USA" - stricter, fewer rows
ORat least one test is true"Canada or UK" - looser, more rows
NOTthe test is false"NOT cancelled" - everything except the match
LiveAND - both tests must pass3 min

This finds customers who are on the Pro plan and based in the USA - both conditions, joined by AND. Because both must be true, AND narrows the result. Try changing 'USA' to 'UK', or swap AND for OR and watch the row count grow.

SELECT name, city
FROM customers
WHERE plan = 'Pro' AND country = 'USA';
LiveOR - either test passes3 min

Here OR keeps a customer if they are in Canada or the UK. Notice you repeat the column name on both sides of OR - country = 'Canada' OR country = 'UK', not country = 'Canada' OR 'UK'. Part 3 shows IN, a tidier way to write exactly this.

SELECT name, country
FROM customers
WHERE country = 'Canada' OR country = 'UK';
Mixing AND with OR? Use parentheses When a query has both, SQLite evaluates AND before OR - just like times-before-plus in maths. Wrap the OR group in parentheses to be explicit: WHERE plan = 'Pro' AND (country = 'Canada' OR country = 'UK'). Clear beats clever.
Part 3 · covers Mode "LIKE, IN, BETWEEN, IS NULL", SQLBolt L4, W3Schools "Like/In/Between/Null"

Pattern and set matching: LIKE, IN, BETWEEN, IS NULL 8 min live

Four specialist operators cover the filters that = cannot. LIKE matches text patterns with wildcards. IN checks membership in a list. BETWEEN tests a range, endpoints included. IS NULL finds missing values - and it is the one people get wrong most often.

LiveLIKE - match a text pattern3 min

The % wildcard means "any run of characters". So '%Espresso%' matches any product name that contains "Espresso" anywhere - start, middle, or end. Run it, then try 'Cold%' (starts with Cold) or '%Beans' (ends with Beans).

SELECT name
FROM products
WHERE name LIKE '%Espresso%';
Two wildcards % matches any number of characters; _ matches exactly one. In SQLite, LIKE is case-insensitive for plain ASCII letters, so '%espresso%' matches too.
LiveIN - match any value in a list2 min

Remember the repetitive OR from Part 2? IN collapses it into one clean list. This is the exact same result - Canada or UK customers - written the tidy way. Add 'Germany' to the list and re-run.

SELECT name, country
FROM customers
WHERE country IN ('Canada','UK');
LiveBETWEEN - a range, endpoints included2 min

BETWEEN 15 AND 20 keeps every price from 15 to 20 inclusive of both ends. It is the readable way to write price >= 15 AND price <= 20. Watch the boundaries: a product priced exactly 15 or exactly 20 is kept.

SELECT name, price
FROM products
WHERE price BETWEEN 15 AND 20;
LiveIS NULL - find the missing values3 min

In Daybreak, only Coffee products have a roast; Equipment and Add-on rows store NULL there. To find them you must write roast IS NULL - never roast = NULL. This is the number-one beginner trap, so it earns its own callout below.

SELECT name
FROM products
WHERE roast IS NULL;
Real world

Why = NULL silently returns nothing. NULL means "unknown", and in SQL anything compared to unknown is itself unknown - never true. So roast = NULL is never true for any row and quietly returns zero rows, no error. Teams have shipped reports that "found no missing data" for months because of exactly this. Always use IS NULL and IS NOT NULL.

✗ roast = NULL NULL compared to NULL is unknown Row is never counted as true Returns zero rows - no error, ever ✓ roast IS NULL Checks for a missing value directly Matches Equipment and Add-on rows Coffee rows (with a roast) are skipped roast = NULL silently returns zero rows every time - never an error, just nothing. Only Coffee products carry a roast; Equipment and Add-on store NULL - IS NULL finds them.
🔍 Click to zoom - roast = NULL always fails silently; roast IS NULL finds the gap
Demo 1 of 2

Build-along: a customer segment ★ 12 min · everyone builds

Daybreak's marketing lead wants a segment: "our Pro customers in the USA, newest signups first." That is two filters joined by AND, plus an ORDER BY from session b1. Let us assemble it one clause at a time.

Start with the table: SELECT name, city, signup_date FROM customers; - everyone, all columns you care about.

Add the first filter: WHERE plan = 'Pro'. Now only Pro customers remain.

Tighten with AND country = 'USA'. Both tests must pass - the segment narrows.

Order it: ORDER BY signup_date DESC puts the newest signups on top. That is the segment.

SELECT name, city, signup_date
FROM customers
WHERE plan = 'Pro' AND country = 'USA'
ORDER BY signup_date DESC;
Real world

Every "audience" is a WHERE clause. The segments marketing teams obsess over - "high-value UK subscribers", "at-risk trial users" - are, underneath the dashboard, exactly this: a few WHERE conditions and a sort. Learn to write them fast and you become the person who answers "can you pull a list of..." in thirty seconds.

Demo 2 of 2

Your turn: three filters, your queries ★ 10 min · build your own

No hand-holding now. Each editor answers one question - run it, tweak it, break it, fix it. That run-fix-run loop is the whole skill, and errors are cheap here.

LiveQ1 · Coffees under $183 min

Two filters joined by AND: the category is Coffee, and the price is below 18. Run it, then loosen the price to 25 and see who joins.

SELECT name, price
FROM products
WHERE category = 'Coffee' AND price < 18;
LiveQ2 · The equipment items (roast is missing)3 min

Equipment has no roast, so filter on both the category and the missing value. Notice the IS NULL - never = NULL.

SELECT name, category, roast
FROM products
WHERE category = 'Equipment' AND roast IS NULL;
Self-studyQ3 · Customers who signed up in 20262 min

A neat trick: dates are stored as text like '2026-03-14', so LIKE '2026%' matches every date that starts with 2026. Later sessions use proper date functions, but this pattern is handy and readable.

SELECT name, signup_date
FROM customers
WHERE signup_date LIKE '2026%'
ORDER BY signup_date;
Homework

Try it yourself - this week ◐ 20-30 min total

Source material

Official sources covered

This session teaches the filtering core of the major free SQL curricula, run on live Daybreak data instead of screenshots. Certificates and graded problem sets stay on the official sites (linked below). This page covers:

Mode SQL Tutorial - Basic: WHERE, comparison & logical operators, LIKE, IN, BETWEEN, IS NULLParts 1-3 · the full WHERE toolkit, run live
SQLBolt Lessons 2-4 - filtering, AND/OR, complex filteringParts 1-3 · same operators, editable here
W3Schools - Where, And/Or/Not, Like, In, Between, NullParts 1-3 · syntax reference stays linked on W3Schools
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · What does a WHERE clause do?

WHERE filters rows - it tests each row and keeps the ones that pass, before grouping happens. SELECT picks columns; ORDER BY sorts.

2 · WHERE name LIKE '%Espresso%' matches names that...

The % wildcards on both sides mean "any characters before and after", so it matches any name containing Espresso anywhere in it.

3 · How do you filter for rows where roast has no value?

Use IS NULL, never = NULL. Comparing anything to NULL yields "unknown", never true, so = NULL silently returns zero rows with no error.

Builder session 2 cheat sheet · pin this

WHEREFilters rows. Sits after FROM, tests each row, keeps the ones that pass.
Comparison= != > < >= <=. Text in single quotes ('Coffee'), numbers bare (18).
AND / OR / NOTAND = both, OR = either, NOT = flip. Wrap OR groups in parentheses when mixing.
LIKEText patterns. % = any characters, _ = one character. '%Espresso%' = contains Espresso.
INMembership in a list. country IN ('Canada','UK') = tidy OR.
BETWEENA range, both endpoints included. price BETWEEN 15 AND 20.
IS NULLFinds missing values. Always IS NULL / IS NOT NULL, never = NULL.
Running projectYou can now carve any slice of Daybreak. Next: b3 counts and sums those slices.