SELECT WHERE ORDER BY Aggregates soon GROUP BY soon
Chapter 2 — Filter Rows

WHERE: ask for exactly what you need.

SELECT shows you columns. WHERE decides which rows. It's the most-used SQL clause in analytics, reporting, and every real query you'll ever write.

Chapter 2 30+ min practice 35 Questions Inline Playground
01
Story
02
Anatomy
03
Examples
04
Gotchas
05
Practice
filter_students.sql ▶ Run
4 rows — Simran (61) filtered out
namescore
Arjun88
Priya72
Raj95
Karan80
01 4 min read

SELECT shows you everything. That's the problem.

You're a freshly hired data analyst. First task from your manager: "Pull everyone from Delhi with a score above 75."

You've just learned SELECT. You run SELECT * FROM students; — five rows drop on screen. You read each one. You compare. You mentally cross out Priya (Mumbai), Raj (Pune), Karan (Chennai), Simran (score: 61). You find Arjun. Done.

Now imagine that table has 10,000 rows. Then 500,000. Then 10 million. The manual-scan approach collapses at row 21.

WHERE is what you were missing. One extra line, and the database does all the scanning for you — instantly, on any size table.
How it works
WHERE evaluates every row, one at a time. For each row, it asks: "Does this row pass my condition?" If the answer is TRUE → the row is included in your results. If the answer is FALSE or NULL → the row is silently dropped. That's the entire mechanism.

Here's the same query — without WHERE and with WHERE. Click any condition below to see which rows pass:

Try a condition:
All 5 rows (no WHERE)
Arjun · Delhi · 88
Priya · Mumbai · 72
Raj · Pune · 95
Simran · Delhi · 61
Karan · Chennai · 80
WHERE
city = 'Delhi'
2 rows pass
Arjun · Delhi · 88
Priya · Mumbai · 72
Raj · Pune · 95
Simran · Delhi · 61
Karan · Chennai · 80
02 3 min read

Real tables don't have 5 rows.

Every production database you'll encounter in a job has tables with thousands, millions, even billions of rows. No analyst, no application, no dashboard ever reads all of them. Every real SQL query filters.

Industry reality
In data analytics roles, over 85% of SQL queries contain a WHERE clause. It's not advanced SQL — it's the baseline. A query without WHERE is a table dump, not analysis. WHERE is what makes SQL useful for answering business questions.

Think about what every real question sounds like:

Business questions → SQL WHERE clauses
-- "Show me customers from Mumbai"
WHERE city = 'Mumbai'

-- "Sales above ₹50,000 this quarter"
WHERE amount > 50000

-- "Users who haven't logged in for 30 days"
WHERE last_login < '2024-01-01'

-- "Products that are out of stock"
WHERE stock = 0 OR stock IS NULL
Safe like SELECT
WHERE only controls which rows you read. It never modifies data. Like SELECT, you can run any WHERE query anywhere, any number of times — it is completely safe.
03 5 min read

A WHERE query has exactly 5 moving parts.

Query Template
SELECT column(s) · FROM table · WHERE column operator value;

Understanding the structure prevents 80% of syntax errors. Here's the full query broken into its components:

Diagram 1 — Full query structure
SELECT name, score
Columns
what to show
  
FROM students
Table
where data lives
  
WHERE score > 70
Filter
which rows pass

Now zoom into the WHERE condition itself — every condition has exactly 3 parts:

Diagram 2 — Condition breakdown
WHERE
Keyword
starts the filter
  
score
Column
what to test
  
>
Operator
comparison type
  
70
Value
right-hand side
Pattern to memorize
Column — Operator — Value. Every WHERE condition follows this pattern. If you can't find all three parts, that's your bug. WHERE > 70 is broken — missing the column. WHERE score is broken — missing the operator and value.

One more thing beginners get wrong: SQL evaluates clauses in this order — not the order you write them:

Step 1
FROM
Load the table
Step 2
WHERE
Filter rows
Step 3
SELECT
Choose columns
Practical result
You can filter on a column you never show. SELECT name FROM students WHERE score > 80 is valid — score is used in WHERE but doesn't appear in the result. WHERE runs before SELECT, so the column just needs to exist in the table.
04 6 min read

14 operators. You'll use 6 constantly.

Equality & Comparison
=
Equal to
Exact match. Works for numbers and text.
score = 80
!=  or  <>
Not equal to
Excludes matching rows. Both symbols work.
city != 'Delhi'
>
Greater than
Strictly above. Does not include boundary.
score > 80
>=
Greater or equal
Above or exactly at boundary. Inclusive.
score >= 80
<
Less than
Strictly below. Does not include boundary.
score < 70
<=
Less or equal
Below or exactly at boundary. Inclusive.
score <= 70
Range & List
BETWEEN
In a range
Inclusive on both ends. Same as >= low AND <= high.
score BETWEEN 70 AND 90
NOT BETWEEN
Outside a range
Returns rows where the value is below low or above high.
score NOT BETWEEN 70 AND 90
IN
Matches any in list
Shorthand for multiple OR conditions. List in parentheses.
city IN ('Delhi','Mumbai')
NOT IN
Matches none in list
Excludes any value that appears in the list.
city NOT IN ('Pune')
Pattern Matching
LIKE
Wildcard match
Uses % (any chars) and _ (exactly 1 char) as wildcards.
name LIKE 'A%'
NOT LIKE
Invert wildcard match
Returns rows where the pattern does NOT match.
city NOT LIKE 'D%'
REGEXP
Regular expression
Full regex patterns — far more powerful than LIKE. MySQL / SQLite.
name REGEXP '^[AK]'

LIKE patterns — the two wildcards you need to know:

Pattern visualiser — see which students match each pattern:

NULL Checks
IS NULL
Value is missing
The only correct way to check for NULL. = NULL does not work.
score IS NULL
IS NOT NULL
Value exists
Returns rows that have an actual value in the column.
score IS NOT NULL

The NULL trap — this catches everyone at least once:

Wrong — returns 0 rows silently
WHERE score = NULL
NULL can't be compared with =. In SQL, NULL = NULL evaluates to NULL (not TRUE). This query matches nothing — no error, just empty results.
Correct — use IS NULL
WHERE score IS NULL
IS NULL is the dedicated NULL check. It correctly finds rows where score has no value stored. Pair with IS NOT NULL to find rows that do have a value.
Logical Operators — Combine Conditions
AND
Both must be true
Stricter filter — fewer rows pass. Both conditions required.
city='Delhi' AND score>70
OR
Either can be true
More permissive — more rows pass. Only one condition required.
city='Delhi' OR city='Mumbai'
NOT
Negate condition
Inverts the result of any condition following it.
NOT city = 'Delhi'

AND vs OR — same two conditions, completely different row counts:

AND — both must be true
city = 'Delhi' AND score > 80
Arjun
Delhi ✓ · score 88 > 80 ✓
Priya
Mumbai ✗ — city fails
Raj
Pune ✗ — city fails
Simran
Delhi ✓ · score 61 ✗ — both needed
Karan
Chennai ✗ — city fails
→ 1 row passes — AND is strict
OR — either is enough
city = 'Delhi' OR score > 80
Arjun
Delhi ✓ · score 88 ✓ — both match
Priya
Mumbai ✗ · score 72 ✗
Raj
Pune ✗ · score 95 > 80 ✓
Simran
Delhi ✓ — one condition enough
Karan
Chennai ✗ · score 80 not > 80 ✗
→ 3 rows pass — OR is permissive
⚠ AND runs before OR — parentheses control grouping

SQL evaluates AND before OR, the same way multiplication runs before addition in math. Without parentheses, this causes silent logic errors:

Ambiguous — AND groups first
WHERE city = 'Delhi' OR city = 'Mumbai' AND score > 70
Reads as: Delhi OR (Mumbai AND score>70). All Delhi rows pass regardless of score — Simran (61) slips through.
Explicit — parentheses lock intent
WHERE (city = 'Delhi' OR city = 'Mumbai') AND score > 70
Now AND applies to both cities. Only Delhi/Mumbai students with score above 70 pass. Simran (61) is correctly excluded.
Subquery Operators
EXISTS
Subquery returns rows
TRUE if the inner SELECT returns at least one row. Used with correlated subqueries.
WHERE EXISTS (SELECT 1 FROM ...)
NOT EXISTS
Subquery returns nothing
TRUE if the inner SELECT returns zero rows. Opposite of EXISTS.
WHERE NOT EXISTS (SELECT 1 FROM ...)
REGEXP — The Case of the Messy Catalog

You just joined ShopNest — India's newest e-commerce startup — as their first data analyst. The product catalog has 10,000 SKUs and zero consistency. Product names were entered by 12 different vendors. SKUs follow a loose pattern. Nobody agreed on anything.

Monday morning, your manager Rahul drops a sticky note on your desk with 6 tasks. "Need this filtered by noon. Brand partnership meeting." He's already walking away.

You open the products table. You try LIKE. It starts breaking immediately.

LIKE gives you two wildcards: % and _. REGEXP gives you a language. And today, you need a language.

The table you're working with — 10 rows in dev, 10,000 in prod:

Task 1 Find all Apple products
Rahul says: "Pull all Apple products for the brand partnership deck."

You think: easy. You try LIKE with 'Apple'. Zero rows. Because no product name says "Apple" — they say "iPhone" and "MacBook". But the SKU always starts with APL for Apple. REGEXP to the rescue.

LIKE — 0 rows, no error
WHERE name LIKE '%Apple%'
No row says "Apple". You'd need LIKE '%iPhone%' OR LIKE '%MacBook%' — and that breaks when they add iPad, AirPods, Watch…
REGEXP — 2 rows, perfect
WHERE sku REGEXP '^APL'
^APL means: SKU must START with APL. Any new Apple product with APL prefix is automatically included.
iPhone 14 Pro
APL-IP14P-256 ✓ starts with APL
Samsung Galaxy S23
SAM-GS23-128 ✗
Nike Air Max 90
NIK-AM90-BLK ✗
Adidas Ultraboost 22
ADI-UB22-WHT ✗
MacBook Pro 14
APL-MBP14-512 ✓ starts with APL
HP Pavilion 15
HP-PAV15-8GB ✗
^
Start Anchor
The pattern must match at the very beginning of the string. Without ^, REGEXP 'APL' would also match strings like "NAPL" or "SNAPL" anywhere in the value.
SQL — Try it
SELECT name, sku
FROM products
WHERE sku REGEXP '^APL';
Task 2 Find versioned products — anything with a model number
Rahul says: "I need all products that have a version or model number in the name — like iPhone 14, Galaxy S23, Air Max 90. Exclude generic names."

A "version number" means: there's at least one digit in the product name. LIKE can't express "contains any digit". REGEXP handles it in 3 characters.

LIKE — impossible to express
WHERE name LIKE '%0%'
OR name LIKE '%1%'
-- ... 8 more lines for 2–9
You'd need 10 separate LIKE conditions, one per digit. Ugly, slow, and guaranteed to be wrong one day.
REGEXP — one expression
WHERE name REGEXP '[0-9]'
[0-9] is a character class. It matches any single digit. If the name contains even one digit anywhere, this row passes.
iPhone 14 Pro
contains digit ✓
Samsung Galaxy S23
contains digit ✓
Nike Air Max 90
contains digit ✓
Adidas Ultraboost 22
contains digit ✓
MacBook Pro 14
contains digit ✓
HP Pavilion 15
contains digit ✓
Levi 511 Slim Jeans
contains digit ✓
Allen Solly Formal Shirt
no digits ✗
OnePlus 11R 5G
contains digit ✓
Reebok Classic Leather
no digits ✗
[0-9]
Character Range
Matches any single character between 0 and 9. No anchor means it matches a digit anywhere in the string. Same idea: [a-z] = any lowercase letter, [A-Za-z] = any letter at all.
SQL — Try it
SELECT name, sku
FROM products
WHERE name REGEXP '[0-9]';
Task 3 Apple OR Samsung only — for a comparison report
Rahul says: "Apple and Samsung only. The investor wants a head-to-head comparison. Two brands, one query."

Two separate LIKE conditions would work — but REGEXP alternation | is cleaner and scales to 10 brands just as easily as 2.

LIKE — works but doesn't scale
WHERE sku LIKE 'APL%'
OR sku LIKE 'SAM%'
Two lines today. Add OnePlus tomorrow — three lines. Add 10 brands — ten lines. Every addition is a new line of SQL.
REGEXP — one expression, unlimited brands
WHERE sku REGEXP '^(APL|SAM)'
(APL|SAM) means: match APL or SAM at the start. Add OnePlus: ^(APL|SAM|OPL). One expression, one edit.
iPhone 14 Pro
APL- ✓ Apple
Samsung Galaxy S23
SAM- ✓ Samsung
Nike Air Max 90
NIK- ✗
MacBook Pro 14
APL- ✓ Apple
HP Pavilion 15
HP- ✗
OnePlus 11R 5G
OPL- ✗ (not in list)
(a|b)
Alternation — OR
The | symbol means OR inside a pattern. Wrap options in () to group them. You can add as many alternatives as you need: (APL|SAM|OPL|NIK).
SQL — Try it
SELECT name, sku, price
FROM products
WHERE sku REGEXP '^(APL|SAM)';
Task 4 All footwear brands — Nike, Adidas, Reebok
Rahul says: "Get me every footwear item — Nike, Adidas, Reebok. Don't filter by category column, someone entered 'footwear' with a capital F and it's inconsistent."

The category column is unreliable. But the SKU prefix is consistent — NIK, ADI, RBK. REGEXP handles all three in one shot.

Category column — unreliable
WHERE category = 'Footwear'
Misses rows entered as 'footwear', 'FOOTWEAR', or 'Foot Wear'. Data entered by vendors = never consistent.
SKU prefix — always reliable
WHERE sku REGEXP '^(NIK|ADI|RBK)'
Vendors can misspell category names but they can't change SKU prefixes — those are set by the system at product creation.
iPhone 14 Pro
APL- ✗
Nike Air Max 90
NIK- ✓
Adidas Ultraboost 22
ADI- ✓
Levi 511 Slim Jeans
LEV- ✗
Reebok Classic Leather
RBK- ✓
OnePlus 11R 5G
OPL- ✗
SQL — Try it
SELECT name, sku, price
FROM products
WHERE sku REGEXP '^(NIK|ADI|RBK)';
Task 5 SKU audit — find all standard 3-letter prefix SKUs
Rahul says: "Our SKU format should be 3 uppercase letters, then a hyphen, then more stuff. The HP product is using 2 letters — 'HP-'. Find any SKU that doesn't follow the 3-letter standard so we can flag it."

You need to match SKUs that do not start with exactly 3 uppercase letters. Use [A-Z]{3}- to define the correct format, then find rows that don't match it.

LIKE — can't count letters exactly
WHERE sku NOT LIKE '___-%'
___ (3 underscores) means any 3 chars — but includes digits and lowercase too. LIKE can count positions but can't say "only uppercase letters".
REGEXP — precise format check
WHERE sku NOT REGEXP '^[A-Z]{3}-'
[A-Z]{3} means exactly 3 uppercase letters. NOT REGEXP flips it — returns SKUs that DON'T match the standard format.
iPhone 14 Pro
APL-IP14P-256 → standard ✓
Samsung Galaxy S23
SAM-GS23-128 → standard ✓
Adidas Ultraboost 22
ADI-UB22-WHT → standard ✓
HP Pavilion 15
HP-PAV15-8GB → only 2 letters ⚠
[A-Z]{3}
Range + Exact Count
[A-Z] matches any uppercase letter. {3} means exactly 3 of the preceding element must appear. Together: "exactly 3 uppercase letters in a row." Use {2,4} for a range of 2 to 4.
SQL — Try it
-- Find SKUs that don't follow the 3-letter prefix standard
SELECT name, sku
FROM products
WHERE sku NOT REGEXP '^[A-Z]{3}-';
Task 6 Highlight "5G" and "Pro" products for the premium landing page
Rahul says: "Marketing wants a premium landing page. Pull everything with '5G' or 'Pro' in the product name — those are the premium tier."

Two search terms, one column — alternation with no anchor. You don't need ^ here because "Pro" and "5G" can appear anywhere in the name.

LIKE — two conditions needed
WHERE name LIKE '%Pro%'
OR name LIKE '%5G%'
Works, but verbose. Add "Ultra", "Max", "Plus" next month and it grows to 5 lines.
REGEXP — clean alternation
WHERE name REGEXP '5G|Pro'
No ^ or $ — the pattern can match anywhere in the string. Add tiers: '5G|Pro|Ultra|Max'.
iPhone 14 Pro
contains 'Pro' ✓
Samsung Galaxy S23
neither ✗
Nike Air Max 90
neither ✗
MacBook Pro 14
contains 'Pro' ✓
Levi 511 Slim Jeans
neither ✗
OnePlus 11R 5G
contains '5G' ✓
a|b
Alternation — No Anchor
When there's no ^, the pattern searches anywhere inside the string — same idea as LIKE '%Pro%' but you can chain alternatives: '5G|Pro|Ultra|Max' without adding more OR lines.
SQL — Try it
SELECT name, sku, price
FROM products
WHERE name REGEXP '5G|Pro';

All 6 tasks done. Here's every pattern you used — and the full reference:

^
Start of string
Anchors to the beginning. ^APL = must start with APL.
Used in Tasks 1, 3, 4, 5
$
End of string
Anchors to the end. n$ = must end with n.
WHERE name REGEXP 'n$'
.
Any single character
Matches exactly one of any character. Combine with {} for exact lengths.
^.{5}$ = exactly 5 chars
*
Zero or more
The preceding element can appear 0 or more times.
A* = A, AA, AAA, or nothing
+
One or more
Like * but requires at least one occurrence.
A+ = A, AA, AAA (not empty)
?
Optional (0 or 1)
Makes the preceding element optional.
colou?r = color or colour
[abc]
Character class
Matches any one character in the brackets.
[AKP] = A or K or P
[^abc]
Negated class
Matches any character NOT in the brackets.
[^aeiou] = not a vowel
[A-Z]
Character range
Any char in the range. [0-9] = any digit. Used in Task 2 & 5.
Used in Tasks 2, 5
a|b
Alternation (OR)
Matches either option. Used in Tasks 3, 4, 6.
APL|SAM = Apple or Samsung
(abc)
Grouping
Groups patterns. Usually paired with | for multi-option alternation.
(APL|SAM|OPL)
{n}
Exact count
The preceding element must appear exactly n times.
[A-Z]{3} = exactly 3 letters
Which databases support REGEXP?
MySQL / MariaDB: WHERE sku REGEXP '^APL' — native, no setup.
SQLite (this playground): REGEXP is enabled here via a registered JavaScript function.
PostgreSQL: uses ~ (case-sensitive) or ~* (case-insensitive) — WHERE sku ~ '^APL'.
SQL Server: no native REGEXP. Use LIKE for simple patterns, or PATINDEX with limited wildcards.
05 6 min read

Ten examples. Each one adds something new.

All examples use the same students table you already know: Arjun (Delhi/88), Priya (Mumbai/72), Raj (Pune/95), Simran (Delhi/61), Karan (Chennai/80).

Jump to:
Ex 1 Basic equality — filter by city

The most common WHERE clause. Text values must be in single quotes. When using =, comparisons are case-sensitive — 'Delhi' and 'delhi' are different values. (Note: LIKE is case-insensitive for plain letters in SQLite.)

SQL
SELECT * FROM students WHERE city = 'Delhi';
Output — 2 rows pass the condition
idnamecityscore
1ArjunDelhi88
4SimranDelhi61
Ex 2 Numeric comparison — filter by score

Numbers don't need quotes. > means strictly greater than — Karan (80) does not qualify for score > 80. Use >= to include 80.

SQL
SELECT name, score FROM students WHERE score > 80;
Output — only Arjun (88) and Raj (95)
namescore
Arjun88
Raj95
Ex 3 AND — both conditions must be true

AND tightens the filter. Simran is from Delhi but her score (61) fails the second condition — AND excludes her. Only Arjun passes both.

SQL
SELECT name, city, score FROM students
WHERE city = 'Delhi' AND score > 70;
Output — Simran (Delhi but score 61) excluded
namecityscore
ArjunDelhi88
Ex 4 IN — match any value in a list

IN is cleaner than writing multiple OR conditions. Always wrap the list in parentheses. Equivalent to city = 'Delhi' OR city = 'Mumbai'.

SQL
SELECT name, city FROM students
WHERE city IN ('Delhi', 'Mumbai');
Output — 3 rows (Delhi ×2, Mumbai ×1)
namecity
ArjunDelhi
PriyaMumbai
SimranDelhi
Ex 5 LIKE — match a pattern, not an exact value

% is a wildcard meaning "zero or more characters." 'A%' means "starts with A, then anything." Only Arjun qualifies here.

SQL
SELECT name FROM students WHERE name LIKE 'A%';
Output — only names starting with A
name
Arjun
Ex 6 BETWEEN — inclusive range filter

BETWEEN low AND high includes both endpoints. It's equivalent to score >= 60 AND score <= 85. Raj (95) is above 85 and excluded.

SQL
SELECT name, score FROM students
WHERE score BETWEEN 60 AND 85;
Output — scores from 60 to 85 inclusive
namescore
Priya72
Simran61
Karan80
Ex 7 LIKE — ends-with pattern

Putting % at the start anchors the pattern to the end of the value. '%n' matches any name ending in the letter n. Three out of five students qualify — Arjun, Simran, and Karan all end with n.

SQL
SELECT name FROM students WHERE name LIKE '%n';
Output — 3 names end with 'n'
name
Arjun
Simran
Karan
Ex 8 LIKE — contains pattern (% on both sides)

Wrapping a substring with % on both sides matches it anywhere in the value. SQLite's LIKE is case-insensitive for ASCII letters — '%ar%' matches both Arjun (characters 1–2) and Karan (characters 2–3).

SQL
SELECT name FROM students WHERE name LIKE '%ar%';
Output — 'ar' found in Arjun (pos 1–2) and Karan (pos 2–3)
name
Arjun
Karan
Ex 9 NOT LIKE — exclude a pattern

Prefix LIKE with NOT to invert the match — return rows where the pattern does not apply. This query excludes any student from a city starting with 'D' (Delhi), so only Mumbai, Pune, and Chennai students appear.

SQL
SELECT name, city FROM students
WHERE city NOT LIKE 'D%';
Output — excludes Delhi students (city starts with D)
namecity
PriyaMumbai
RajPune
KaranChennai
Ex 10 AND + OR with parentheses — explicit grouping

Mixing AND and OR requires parentheses to make intent explicit. Without them, AND runs first (higher precedence). This query correctly finds Delhi or Mumbai students who also scored above 70 — Simran (Delhi, 61) is excluded because 61 fails the score condition.

SQL
SELECT name, city, score FROM students
WHERE (city = 'Delhi' OR city = 'Mumbai') AND score > 70;
Output — Simran (Delhi, 61) excluded because 61 is not > 70
namecityscore
ArjunDelhi88
PriyaMumbai72
06 4 min read

Nine traps — every beginner hits at least two.

WHERE score = NULL — the silent trap
This returns 0 rows with no error. NULL cannot be compared with =. SQL evaluates NULL = NULL as NULL (not TRUE), so nothing matches. Always use IS NULL or IS NOT NULL.
Missing quotes around text values
WHERE city = Delhi throws an error because SQL thinks Delhi is a column name. Always wrap text values in single quotes: WHERE city = 'Delhi'. Numbers need no quotes: WHERE score = 80.
Using == (Python habit) instead of =
WHERE score == 80 is invalid SQL. SQL equality is single =. Double == is Python and JavaScript. This one bites almost every developer switching to SQL for the first time.
BETWEEN 70 TO 90 — wrong connector
The connector inside BETWEEN is AND, not TO. BETWEEN 70 TO 90 is a syntax error. Think of it as: "score is between 70 AND 90." Always: BETWEEN low AND high.
AND is stricter, OR is more permissive
AND requires every condition to pass — fewer rows come through. OR only needs one condition to pass — more rows come through. Confused? Ask yourself: "am I narrowing down or expanding the results?" That's AND vs OR.
Each condition needs its own column name
WHERE city = 'Delhi' AND = 'Mumbai' is broken. SQL doesn't inherit the column from the previous condition. Write it in full: WHERE city = 'Delhi' OR city = 'Mumbai'. Or cleaner: WHERE city IN ('Delhi','Mumbai').
NOT IN with a NULL in the list — silent zero rows
WHERE city NOT IN ('Delhi', NULL) returns 0 rows with no error. SQL can never confirm that any value is "not equal to NULL" — the comparison resolves to NULL (unknown), which evaluates as FALSE. If NULLs may exist, filter them separately with IS NOT NULL.
AND/OR precedence — AND groups first without parentheses
WHERE city='Delhi' OR city='Mumbai' AND score>70 is not what it looks like. SQL runs the AND first: it becomes Delhi OR (Mumbai AND score>70). Simran from Delhi (score 61) still passes. Fix: add parentheses — WHERE (city='Delhi' OR city='Mumbai') AND score>70.
LIKE without a wildcard behaves like = but case-insensitive
WHERE name LIKE 'arjun' returns Arjun in SQLite, but WHERE name = 'arjun' returns nothing (case-sensitive). Use = for exact case-sensitive matching, and LIKE when you want case-insensitive lookup or need wildcard patterns.
07 2 min read

What you just learned.

Quick Reference WHERE — what you need to know
  • WHERE goes after FROM and filters rows. Only rows where the condition is TRUE appear in the result.
  • Every condition has 3 parts: column — operator — value. If one is missing, that's your bug.
  • Text values need single quotes ('Delhi'). Numbers do not (80). Forgetting quotes is the #1 beginner error.
  • Never use = NULL. It silently returns 0 rows. Use IS NULL or IS NOT NULL.
  • AND = both conditions required (stricter, fewer rows). OR = either condition sufficient (more permissive, more rows).
  • BETWEEN includes both endpoints. BETWEEN 70 AND 90 is the same as >= 70 AND <= 90.WHERE score BETWEEN 70 AND 90
  • IN is cleaner than multiple ORs. city IN ('Delhi','Mumbai') is easier to read than city = 'Delhi' OR city = 'Mumbai'.
  • WHERE never changes data. Like SELECT, it only reads. Safe to run anywhere, any number of times.
  • REGEXP is LIKE's powerful sibling. Use ^ for start, $ for end, [AK] for character classes, . for any char. Available in MySQL, SQLite; PostgreSQL uses ~; SQL Server uses LIKE.
  • NOT BETWEEN and NOT LIKE invert their operators exactly. score NOT BETWEEN 70 AND 90 returns scores below 70 or above 90.
08 30+ min

50 WHERE Questions

Work in order — each question builds on the last. Q01–Q35 use the students table · Q36–Q45 use the Employees table · Q46–Q50 are REGEXP challenges.

12 MCQ — concept checks 11 Fix-It — spot the bug 27 Write — build from scratch
0 / 50 completed Start with Q01
SSMS Setup Scripts
Paste and run each script in SQL Server Management Studio before practising on your own machine. The in-browser playground loads these automatically — you only need this for SSMS.
Q01 – Q35 students table  ·  5 rows
SQL Server — paste into SSMS
-- Q01–Q35  |  students table
DROP TABLE IF EXISTS students;

CREATE TABLE students (
    id    INT,
    name  VARCHAR(50),
    city  VARCHAR(50),
    score INT
);

INSERT INTO students VALUES
(1, 'Arjun',  'Delhi',   88),
(2, 'Priya',  'Mumbai',  72),
(3, 'Raj',    'Pune',    95),
(4, 'Simran', 'Delhi',   61),
(5, 'Karan',  'Chennai', 80);
Q36 – Q50 Employees table  ·  20 rows  ·  real-world dataset
SQL Server — paste into SSMS
-- Q36–Q45  |  Employees table (20 rows)
DROP TABLE IF EXISTS Employees;

CREATE TABLE Employees (
    EmployeeID   INT            PRIMARY KEY,
    FirstName    VARCHAR(50),
    LastName     VARCHAR(50),
    Department   VARCHAR(50),
    JobTitle     VARCHAR(100),
    Salary       DECIMAL(10,2),
    HireDate     DATE,
    City         VARCHAR(50),
    IsActive     BIT,
    ManagerID    INT NULL
);

INSERT INTO Employees VALUES
(1,  'Aarav',     'Sharma',   'IT',        'SQL Developer',        72000,  '2019-03-15', 'Mumbai',    1, NULL),
(2,  'Priya',     'Mehta',    'HR',        'HR Manager',           65000,  '2018-07-01', 'Delhi',     1, NULL),
(3,  'Rohan',     'Verma',    'Sales',     'Sales Executive',      45000,  '2021-06-10', 'Pune',      1, 1),
(4,  'Sneha',     'Kapoor',   'IT',        'Data Analyst',         68000,  '2020-01-20', 'Bangalore', 1, 1),
(5,  'Karan',     'Singh',    'Finance',   'Financial Analyst',    75000,  '2017-11-05', 'Mumbai',    1, NULL),
(6,  'Anjali',    'Patel',    'Marketing', 'Marketing Executive',  42000,  '2022-03-01', 'Ahmedabad', 1, 2),
(7,  'Vikram',    'Nair',     'IT',        'Python Developer',     80000,  '2019-08-22', 'Bangalore', 1, 1),
(8,  'Pooja',     'Joshi',    'Sales',     'Sales Manager',        70000,  '2016-05-18', 'Delhi',     1, NULL),
(9,  'Arjun',     'Reddy',    'Finance',   'Senior Accountant',    60000,  '2020-09-30', 'Hyderabad', 0, 5),
(10, 'Meera',     'Iyer',     'HR',        'HR Executive',         38000,  '2023-01-15', 'Chennai',   1, 2),
(11, 'Rahul',     'Gupta',    'IT',        'BI Developer',         77000,  '2018-04-10', 'Mumbai',    1, 1),
(12, 'Divya',     'Shah',     'Marketing', 'Content Strategist',   48000,  '2021-11-20', 'Pune',      1, 6),
(13, 'Nikhil',    'Desai',    'Sales',     'Sales Executive',      43000,  '2022-07-05', 'Surat',     0, 8),
(14, 'Kavya',     'Pillai',   'IT',        'Cloud Engineer',       85000,  '2017-02-28', 'Bangalore', 1, NULL),
(15, 'Aditya',    'Bose',     'Finance',   'Finance Manager',      90000,  '2015-10-01', 'Kolkata',   1, NULL),
(16, 'Simran',    'Kaur',     'HR',        'Recruiter',            35000,  '2023-06-01', 'Delhi',     1, 2),
(17, 'Manish',    'Tiwari',   'Marketing', 'SEO Analyst',          40000,  '2022-09-14', 'Lucknow',   1, 6),
(18, 'Ritika',    'Saxena',   'Sales',     'Business Dev Exec',    55000,  '2020-04-17', 'Mumbai',    0, 8),
(19, 'Siddharth', 'Rao',      'IT',        'Database Admin',       NULL,   '2021-08-03', 'Hyderabad', 1, 1),
(20, 'Nisha',     'Malhotra', 'Finance',   'Junior Accountant',    32000,  '2023-03-20', 'Delhi',     1, 15);
Up Next — Chapter 3 ORDER BY — Sort Your Results Learn ASC, DESC, multi-column sorting, and how ORDER BY combines with WHERE.
ORDER BY
Question done!
0%