RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
SQL questions
EasySQL#236 min readJul 11, 2026

WHERE

practice
learning
Practice modeTest yourself instead of reading straight through

Hook: Think of WHERE as the bouncer at the door: only rows that match the rule get into the query result.

Question: What is WHERE in SQL?

Answer: WHERE is the clause you use to filter rows before the final result is returned. It keeps only the records that make a condition true, such as price > 100 or status = 'PAID'. One important detail is that NULL does not behave like a normal value, so you must use IS NULL instead of = NULL.

Interview-Ready Answer: I use WHERE to filter rows in a table or join result before the query finishes. It works with conditions like =, <, IN, BETWEEN, and LIKE, and it is evaluated before grouping and aggregation. A key detail is that NULL needs IS NULL, not = NULL, because SQL treats unknown comparisons differently.

🧠 Memory Map
Memory map — visual summary of this topic

What WHERE does

Detailed Explanation: WHERE is the row filter in SQL. Imagine a huge table as a stack of cards; WHERE removes the cards that do not match your rule. The important idea is that it works on rows, not on already-computed summaries.

  1. SQL starts by finding the rows from FROM and any JOINs.
  2. WHERE checks each candidate row against the condition.
  3. If the condition is true, the row stays; if false, it is removed.
  4. Only after that do later steps like GROUP BY, HAVING, SELECT, ORDER BY, and LIMIT happen.

This is why WHERE cannot use a column alias from SELECT in most databases: the alias does not exist yet when filtering happens. It also means WHERE usually cannot filter on an aggregate like COUNT(*); that belongs in HAVING.

Three-valued logic and NULL

SQL has three truth values: true, false, and unknown. NULL means “missing or unknown,” so status = NULL is not true; it is unknown, which behaves like false in WHERE. That is why the correct test is status IS NULL or status IS NOT NULL.

Common operators

  • =, <, >, <=, >= for direct comparison.
  • AND, OR, NOT for combining rules.
  • IN for a short list of allowed values.
  • BETWEEN for inclusive ranges.
  • LIKE for simple pattern matching.

One very common gotcha is operator precedence. AND binds tighter than OR, so A OR B AND C means A OR (B AND C), not (A OR B) AND C. When in doubt, add parentheses.

WHERE vs HAVING

ClauseFiltersUses aggregates?Typical stage
WHERERowsNoBefore grouping
HAVINGGroupsYesAfter grouping

Use WHERE when you want to reduce the number of raw rows early. Use HAVING when you want to filter after a GROUP BY, such as “groups with more than 10 orders.”

Performance: why WHERE matters

WHERE is not just about correctness; it can be a big performance win. A good, index-friendly predicate is often called sargable (search-arg-able), meaning the database can use an index efficiently. A simple equality or range check on an indexed column can often be close to O(log n + k), where k is the number of matching rows. A full table scan is closer to O(n), which hurts as tables grow.

In real systems, a B-tree index often finds rows in just a few page reads even for millions of rows. But patterns like LIKE '%abc' usually cannot use a normal index well because the leading wildcard hides the start of the value. Also, wrapping an indexed column in a function, such as WHERE DATE(created_at) = ..., often makes the predicate less index-friendly unless you have a matching functional index.

When to use it

Use WHERE whenever you want to narrow a result set early: active users only, orders above a threshold, rows from a date range, or records matching a status list. That early filtering keeps queries easier to read and often faster to run.

Real-World Story: Imagine a checkout service for an e-commerce app. A data analyst needs to see only successful orders over $100 that were created this week, so the dashboard query uses WHERE status = 'PAID' AND amount > 100. This keeps refunded, pending, and test orders out of the report.

What goes wrong when someone misunderstands WHERE? A common bug is writing status = NULL or forgetting parentheses around mixed AND/OR logic. The symptom is either an empty report, or far too many orders showing up. In logs you might see the SQL execute successfully, but the dashboard numbers look suspiciously low or high, and support hears, “Why are canceled orders in the sales chart?”

Another production-style mistake is filtering too late. If you first pull millions of rows and only then try to narrow them in application code, the database does extra work, network traffic grows, and the page may slow down from milliseconds to seconds.

SQL
-- Runnable demo: WHERE filters rows, handles NULL correctly, and shows a precedence trap.
-- This script is written in standard, SQLite-friendly SQL.

DROP TABLE IF EXISTS orders;

CREATE TABLE orders (
  order_id   INTEGER PRIMARY KEY,
  customer   TEXT,
  status     TEXT,
  amount     INTEGER NOT NULL,
  created_at TEXT NOT NULL
);

INSERT INTO orders (order_id, customer, status, amount, created_at) VALUES
  (1, 'Ava',  'PAID',     120, '2024-01-10'),
  (2, 'Ben',  'PENDING',   40, '2024-01-11'),
  (3, 'Cara', 'PAID',      75, '2024-01-12'),
  (4, 'Drew', NULL,       200, '2024-01-12'),
  (5, 'Eli',  'REFUNDED', 120, '2024-01-13'),
  (6, 'Fay',  'PAID',      25, '2024-02-01');

-- 1) Simple row filtering: keep only paid orders above 100.
SELECT order_id, customer, amount
FROM orders
WHERE status = 'PAID'
  AND amount > 100
ORDER BY amount DESC;

-- 2) Failure path: this looks reasonable, but it returns no rows.
-- In SQL, NULL is not equal to anything, not even another NULL.
SELECT order_id, customer, status
FROM orders
WHERE status = NULL;

-- 3) Correct NULL check: use IS NULL.
SELECT order_id, customer, status
FROM orders
WHERE status IS NULL;

-- 4) IN, BETWEEN, LIKE together: a compact, readable filter.
SELECT order_id, customer, status, amount
FROM orders
WHERE status IN ('PAID', 'PENDING')
  AND amount BETWEEN 20 AND 120
  AND customer LIKE 'A%'
ORDER BY order_id;

-- 5) Precedence trap: AND is evaluated before OR.
-- This means: paid orders, OR pending orders above 100.
SELECT order_id, customer, status, amount
FROM orders
WHERE status = 'PAID'
   OR status = 'PENDING' AND amount > 100
ORDER BY order_id;

-- 6) Correct version with parentheses: now BOTH statuses must meet the amount rule.
SELECT order_id, customer, status, amount
FROM orders
WHERE (status = 'PAID' OR status = 'PENDING')
  AND amount > 100
ORDER BY order_id;

Follow-up & Tricky Questions:

  • When does WHERE run compared with GROUP BY? WHERE runs before grouping. It removes raw rows first, and then GROUP BY forms groups from the remaining rows.
  • Why can’t I use a SELECT alias in WHERE? Because the alias is created later in SQL’s logical order. The filter happens before the projection step that creates the alias.
  • What is the difference between WHERE and HAVING? WHERE filters rows, while HAVING filters grouped results. If your condition uses COUNT, SUM, or another aggregate, you usually need HAVING.
  • How does NULL affect WHERE? Comparisons with NULL return unknown, so the row is not kept. Use IS NULL or IS NOT NULL instead.
  • Can WHERE hurt performance? Yes, if the condition prevents index use or if you filter too late. A sargable predicate on an indexed column is much faster than scanning every row.
  • Tricky: Why did A OR B AND C not behave like I expected? Because AND has higher precedence than OR. SQL reads it as A OR (B AND C), so use parentheses when the meaning matters.
  • Tricky: Why did WHERE price = NULL return nothing? Because equality does not match NULL. The correct form is WHERE price IS NULL.
  • Tricky: Can I filter aggregated values in WHERE? Not directly. Aggregates are produced after WHERE, so use a subquery or HAVING instead.

Common Mistakes:

  • Using = NULL instead of IS NULL. Correction: NULL needs special handling because it means unknown, not a normal value.
  • Forgetting parentheses with mixed AND/OR. Correction: make the intended logic explicit so SQL does not surprise you.
  • Trying to use aggregates in WHERE. Correction: filter raw rows in WHERE and grouped results in HAVING.
  • Wrapping indexed columns in functions too early. Correction: write predicates in an index-friendly way when possible, such as range filters on the column itself.

Memory Hook: WHERE is the bouncer at the door: it checks each row before the rest of the query party starts.

Cheat Sheet:

  • WHERE filters rows.
  • It runs before GROUP BY and HAVING.
  • Use IS NULL, not = NULL.
  • AND binds tighter than OR.
  • Good filters can use indexes and run much faster.
  • Use HAVING for aggregate filters.

Practice Tasks:

  • Write a query that returns orders between 50 and 150 with status PAID.
  • Rewrite a query with mixed AND/OR using parentheses so the intent is obvious.
  • Change the demo table to find customers whose names start with C and have a non-null status.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

-- Runnable demo: WHERE filters rows, handles NULL correctly, and shows a precedence trap. -- This script is written in standard, SQLite-friendly SQL. DROP TABLE IF EXISTS orders; CREATE TABLE orders ( order_id INTEGER PRIMARY KEY, customer TEXT, status TEXT, amount INTEGER NOT NULL, created_at TEXT NOT NULL ); INSERT INTO orders (order_id, customer, status, amount, created_at) VALUES (1, 'Ava', 'PAID', 120, '2024-01-10'), (2, 'Ben', 'PENDING', 40, '2024-01-11'), (3, 'Cara', 'PAID', 75, '2024-01-12'), (4, 'Drew', NULL, 200, '2024-01-12'), (5, 'Eli', 'REFUNDED', 120, '2024-01-13'), (6, 'Fay', 'PAID', 25, '2024-02-01'); -- 1) Simple row filtering: keep only paid orders above 100. SELECT order_id, customer, amount FROM orders WHERE status = 'PAID' AND amount > 100 ORDER BY amount DESC; -- 2) Failure path: this looks reasonable, but it returns no rows. -- In SQL, NULL is not equal to anything, not even another NULL. SELECT order_id, customer, status FROM orders WHERE status = NULL; -- 3) Correct NULL check: use IS NULL. SELECT order_id, customer, status FROM orders WHERE status IS NULL; -- 4) IN, BETWEEN, LIKE together: a compact, readable filter. SELECT order_id, customer, status, amount FROM orders WHERE status IN ('PAID', 'PENDING') AND amount BETWEEN 20 AND 120 AND customer LIKE 'A%' ORDER BY order_id; -- 5) Precedence trap: AND is evaluated before OR. -- This means: paid orders, OR pending orders above 100. SELECT order_id, customer, status, amount FROM orders WHERE status = 'PAID' OR status = 'PENDING' AND amount > 100 ORDER BY order_id; -- 6) Correct version with parentheses: now BOTH statuses must meet the amount rule. SELECT order_id, customer, status, amount FROM orders WHERE (status = 'PAID' OR status = 'PENDING') AND amount > 100 ORDER BY order_id;