SQL WHERE Clause Guide
A practical guide to the SQL WHERE clause, including comparison operators, logical conditions, pattern matching, NULL values, and common mistakes.
The SQL WHERE clause is used to filter rows based on one or more conditions. Instead of returning every row from a table, a WHERE clause allows you to select only the records that satisfy a particular requirement.
WHERE is one of the most frequently used SQL clauses. It appears in SELECT queries, but it is also commonly used with UPDATE and DELETE statements. You can use comparison operators, logical operators, ranges, lists, pattern matching, NULL checks, subqueries, and other expressions to build precise filters.
This guide explains how the WHERE clause works, how to combine conditions, how NULL affects filtering, and how to avoid common mistakes.
What Is the SQL WHERE Clause?
The WHERE clause specifies which rows should be included in the result or affected by a statement. Only rows for which the WHERE condition evaluates as true are selected or modified.
SELECT *
FROM users
WHERE active = true;This query returns only users whose active column is true. Without WHERE, the SELECT statement would return every row from users.
Basic WHERE Syntax
SELECT column1, column2
FROM table_name
WHERE condition;The condition can be a simple comparison or a complex expression containing multiple operators and conditions.
SELECT id, name, age
FROM users
WHERE age >= 18;Here, the database evaluates age >= 18 for each row and returns rows where the expression is true.
WHERE with Comparison Operators
Comparison operators are the foundation of many WHERE conditions. They compare a column or expression with another value.
| Operator | Meaning | Example |
|---|---|---|
| = | Equal to | age = 18 |
| <> | Not equal to | status <> 'inactive' |
| != | Not equal to in supported dialects | status != 'inactive' |
| > | Greater than | price > 100 |
| < | Less than | price < 100 |
| >= | Greater than or equal to | age >= 18 |
| <= | Less than or equal to | age <= 65 |
For example, this query finds products costing more than 100:
SELECT name, price
FROM products
WHERE price > 100;Filtering Text Values
Text values are normally written as string literals using single quotes. The exact behavior of comparisons can depend on the database's collation and configuration.
SELECT id, name
FROM users
WHERE country = 'Canada';This returns users whose country value matches the specified string according to the database's comparison rules.
Filtering Numeric Values
Numeric columns can be filtered using standard comparison operators.
SELECT *
FROM products
WHERE price >= 50
AND price <= 200;This query returns products whose price falls between 50 and 200 inclusive.
Filtering Dates
WHERE is frequently used to filter records by dates and timestamps. The exact literal syntax can vary between database systems, so use the format recommended for your database.
SELECT *
FROM orders
WHERE created_at >= '2026-01-01';This condition selects rows whose created_at value is on or after the specified point in time, subject to the database's date and timestamp interpretation.
Using AND
AND combines conditions that must all be true.
SELECT *
FROM products
WHERE price > 50
AND stock > 0;A product must satisfy both conditions to appear in the result. It must cost more than 50 and have stock greater than zero.
You can combine several AND conditions:
SELECT *
FROM users
WHERE active = true
AND age >= 18
AND country = 'Canada';Using OR
OR returns a row when at least one of its conditions is true.
SELECT *
FROM users
WHERE role = 'admin'
OR role = 'editor';This query returns users whose role is either admin or editor.
AND and OR Together
When AND and OR appear in the same expression, operator precedence matters. Parentheses make the intended logic explicit and help prevent mistakes.
SELECT *
FROM users
WHERE active = true
AND (role = 'admin' OR role = 'editor');The parentheses mean that the user must be active and must have either the admin or editor role.
Using NOT
NOT reverses a logical condition.
SELECT *
FROM users
WHERE NOT active = true;In practice, many conditions are clearer when written directly using an appropriate comparison operator or predicate. NOT becomes particularly useful with expressions such as IN, BETWEEN, EXISTS, and LIKE.
The IN Operator
IN checks whether a value matches one of several specified values. It can make a query shorter and clearer than a long sequence of OR conditions.
SELECT *
FROM users
WHERE country IN ('Canada', 'Germany', 'Japan');The same idea could be written with OR, but IN is usually easier to read when checking a value against a list.
SELECT *
FROM users
WHERE country = 'Canada'
OR country = 'Germany'
OR country = 'Japan';NOT IN
NOT IN selects rows whose value does not match any value in the specified list.
SELECT *
FROM users
WHERE country NOT IN ('Canada', 'Germany');NULL values require special attention with NOT IN because SQL uses three-valued logic. If NULL can occur in the compared expression or subquery result, NOT IN may produce results that differ from what you intuitively expect.
The BETWEEN Operator
BETWEEN checks whether a value falls within an inclusive range. The lower and upper boundaries are included.
SELECT *
FROM products
WHERE price BETWEEN 50 AND 100;This includes products with a price of exactly 50 or exactly 100, as well as values between them.
BETWEEN can also be used with dates, but timestamp boundaries require particular care. For example, a condition ending at midnight may unintentionally exclude later times on the same calendar day.
The LIKE Operator
LIKE is used for pattern matching with text. The two most common wildcard characters are % and _, although exact behavior can depend on the database dialect.
| Pattern | Meaning | Example |
|---|---|---|
| 'A%' | Starts with A | Anna |
| '%son' | Ends with son | Johnson |
| '%ann%' | Contains ann | Joanna |
| '_a%' | Second character is a | Mark |
SELECT *
FROM users
WHERE name LIKE 'Ann%';This matches names beginning with Ann, such as Anna or Annie, subject to the database's case-sensitivity and collation rules.
NOT LIKE
NOT LIKE excludes values matching a specified pattern.
SELECT *
FROM users
WHERE email NOT LIKE '%@example.com';This condition excludes email addresses that end with the specified domain pattern.
Checking for NULL
NULL represents an unknown or missing value. It should normally be checked with IS NULL or IS NOT NULL rather than the equality operator.
SELECT *
FROM users
WHERE phone IS NULL;To find rows where a value exists, use IS NOT NULL:
SELECT *
FROM users
WHERE phone IS NOT NULL;SQL Three-Valued Logic
SQL conditions can evaluate to TRUE, FALSE, or UNKNOWN. UNKNOWN commonly appears when an expression involves NULL.
SELECT *
FROM users
WHERE phone = NULL;The comparison does not evaluate to TRUE for rows where phone is NULL. Use IS NULL when you need to find missing values.
Understanding TRUE, FALSE, and UNKNOWN becomes especially important when combining conditions with AND, OR, and NOT.
WHERE with CASE Expressions
A CASE expression can be used inside a WHERE condition when filtering depends on conditional logic.
SELECT *
FROM products
WHERE CASE
WHEN category = 'premium' THEN price > 100
ELSE price > 50
END;The exact use of CASE in predicates can vary in readability and portability between database systems. Often, ordinary AND and OR conditions are clearer when the logic is simple.
WHERE with Functions
Functions can be used in WHERE conditions to transform or inspect values.
SELECT *
FROM users
WHERE LOWER(email) = '[email protected]';This example converts the email value to lowercase before comparing it. Function names and available functions differ between database systems.
WHERE with Subqueries
A subquery can be used inside a WHERE clause when the filter depends on information retrieved by another query.
SELECT *
FROM products
WHERE category_id IN (
SELECT id
FROM categories
WHERE active = true
);The inner query finds active categories. The outer query then returns products whose category_id appears in that result.
WHERE with EXISTS
EXISTS checks whether a subquery returns at least one row. It is useful when the existence of a related record is what matters rather than the actual values returned by the subquery.
SELECT *
FROM customers AS c
WHERE EXISTS (
SELECT 1
FROM orders AS o
WHERE o.customer_id = c.id
);This returns customers for whom at least one matching order exists.
WHERE with NOT EXISTS
NOT EXISTS selects rows for which the subquery finds no matching record.
SELECT *
FROM customers AS c
WHERE NOT EXISTS (
SELECT 1
FROM orders AS o
WHERE o.customer_id = c.id
);This pattern can be used to find customers who have never placed an order.
WHERE in SELECT Statements
The most familiar use of WHERE is filtering rows returned by SELECT.
SELECT id, name, email
FROM users
WHERE active = true
AND age >= 18;Only rows satisfying both conditions are included in the result.
WHERE in UPDATE Statements
WHERE can restrict which rows an UPDATE modifies.
UPDATE users
SET active = false
WHERE last_login < '2025-01-01';Only users satisfying the condition are updated.
WHERE in DELETE Statements
DELETE can also use WHERE to restrict which rows are removed.
DELETE FROM sessions
WHERE expires_at < '2026-01-01';Only expired sessions matching the condition are deleted.
WHERE vs HAVING
WHERE and HAVING both filter data, but they operate at different stages of a query. WHERE normally filters individual rows before grouping, while HAVING filters grouped results after GROUP BY.
SELECT department, COUNT(*) AS employee_count
FROM employees
WHERE active = true
GROUP BY department
HAVING COUNT(*) > 10;Here, WHERE removes inactive employees before the groups are created. HAVING then keeps only departments whose resulting group contains more than 10 employees.
| Clause | Filters | Typical use |
|---|---|---|
| WHERE | Rows | Filter individual records |
| HAVING | Groups | Filter aggregate results |
WHERE and JOINs
WHERE can be used after JOIN clauses to filter the combined result.
SELECT c.name, o.total
FROM customers AS c
JOIN orders AS o
ON c.id = o.customer_id
WHERE o.total > 100;The ON clause defines the relationship between customers and orders. WHERE then filters the joined rows to orders with totals above 100.
With OUTER JOINs, the placement of a condition can change the result. A condition in WHERE can remove rows that an OUTER JOIN would otherwise preserve.
Operator Precedence in WHERE
SQL databases follow rules for evaluating operators. In expressions containing AND and OR, AND generally has higher precedence than OR. Relying on precedence without parentheses can make a query difficult to understand.
SELECT *
FROM products
WHERE category = 'book'
OR category = 'game'
AND price < 50;The condition is generally interpreted as category = 'book' OR both category = 'game' and price < 50. If the intention is to apply the price condition to both categories, use parentheses:
SELECT *
FROM products
WHERE (category = 'book' OR category = 'game')
AND price < 50;Common WHERE Clause Mistakes
Using = Instead of IS NULL
One of the most common mistakes is trying to compare NULL with the equality operator.
WHERE email = NULLUse IS NULL instead:
WHERE email IS NULLForgetting Quotes Around Text
SELECT *
FROM users
WHERE country = Canada;If Canada is a string value, it should normally be quoted:
SELECT *
FROM users
WHERE country = 'Canada';Using OR When AND Is Required
Choosing the wrong logical operator can produce far more rows than expected. If every condition must be satisfied, use AND. If any condition is sufficient, use OR.
Forgetting Parentheses
Complex combinations of AND and OR can easily produce unintended results without explicit grouping.
WHERE active = true
AND role = 'admin'
OR role = 'editor'If the intention is to require active users in either role, use:
WHERE active = true
AND (role = 'admin' OR role = 'editor')Filtering on the Wrong Column
A query can be syntactically valid but logically incorrect if the WHERE condition references the wrong column. This is particularly common in tables containing similar fields such as status, type, category_id, and parent_id.
Overly Broad Conditions
A condition such as status <> 'deleted' may include NULL values differently from what you expect. Similarly, a broad OR condition can unintentionally include many additional rows.
When a filter is important, test it with a SELECT statement first and inspect the affected rows before using the same condition in UPDATE or DELETE.
WHERE Clause Best Practices
- Use parentheses when combining AND and OR conditions.
- Use IS NULL and IS NOT NULL for NULL checks.
- Use IN when comparing a value against a list of alternatives.
- Use BETWEEN when an inclusive range clearly expresses the requirement.
- Use LIKE for pattern matching when appropriate.
- Qualify columns with table aliases in multi-table queries.
- Keep complex conditions formatted across multiple lines.
- Test important filters with SELECT before using them in UPDATE or DELETE.
- Consider SQL dialect differences when using functions and specialized operators.
- Check query execution plans when filtering becomes a performance bottleneck.
Improving WHERE Clause Readability
Readable SQL is easier to verify and maintain. Put each major condition on its own line and indent related expressions consistently.
SELECT id, name, email
FROM users
WHERE active = true
AND age >= 18
AND (
role = 'admin'
OR role = 'editor'
);Formatting makes the logical structure visible. It is much easier to identify which conditions belong together than when the entire WHERE expression is written on a single line.
WHERE Clause and Query Performance
Filtering can significantly reduce the amount of data returned, but the performance of a WHERE condition depends on the database engine, indexes, statistics, data distribution, and the expression itself.
Columns frequently used for selective filters may benefit from appropriate indexes. However, adding indexes indiscriminately can increase storage requirements and write costs. The right indexing strategy depends on the application's workload.
Functions, implicit type conversions, leading wildcard patterns, and complex expressions can affect how efficiently an index can be used. When performance matters, inspect the execution plan rather than relying only on intuition.
A Practical WHERE Debugging Workflow
- Start with the simplest version of the query.
- Add one WHERE condition at a time.
- Check the data types of the columns being compared.
- Verify string and date literal formats for the target database.
- Check NULL behavior explicitly.
- Add parentheses when combining AND and OR.
- Confirm that IN and NOT IN lists contain the expected values.
- Test complex subqueries separately.
- Inspect the number of rows returned after each major condition.
- Use an execution plan when query performance is a concern.
This incremental approach helps distinguish syntax errors from logical mistakes. A query that parses successfully may still need careful testing to ensure that the filter represents the intended business rule.
Frequently Asked Questions
What does the SQL WHERE clause do?
The WHERE clause filters rows based on a condition. Only rows for which the condition evaluates to true are returned by SELECT or affected by statements such as UPDATE and DELETE.
What is the difference between WHERE and HAVING?
WHERE normally filters individual rows before grouping, while HAVING filters groups after GROUP BY. HAVING is commonly used with aggregate conditions such as COUNT or SUM.
How do I check for NULL in SQL?
Use IS NULL to find NULL values and IS NOT NULL to find non-NULL values. Ordinary equality comparisons such as column = NULL should not normally be used for NULL checks.
How do I use multiple conditions in WHERE?
Use AND when all conditions must be true and OR when at least one condition can be true. Parentheses should be used to make complex combinations explicit.
What is the difference between IN and BETWEEN?
IN checks whether a value matches one of several specified values. BETWEEN checks whether a value falls within an inclusive lower and upper boundary.
Can WHERE be used with UPDATE and DELETE?
Yes. WHERE can restrict which rows an UPDATE modifies or which rows a DELETE removes. Always verify the condition carefully before executing destructive statements.
Why does my WHERE condition return unexpected rows?
Common causes include incorrect AND/OR grouping, NULL behavior, wrong column references, data type differences, text comparison rules, and conditions that are broader than intended.
Helpful SQL Filtering Tools
Several types of web-based SQL tools can help when working with WHERE clauses. Query explainers can make complex filtering logic easier to understand, validators can help detect malformed SQL, formatters can improve the readability of nested conditions, and syntax highlighters can make keywords and expressions easier to inspect. Keyword formatting tools can also help keep SQL style consistent across queries.
Conclusion
The SQL WHERE clause is one of the most important tools for controlling which rows a query returns or modifies. It can filter values with comparison operators, combine conditions with AND and OR, match lists with IN, filter ranges with BETWEEN, search text with LIKE, and handle missing values with IS NULL.
The most important part of writing reliable WHERE conditions is understanding the data and the logic behind the filter. Pay particular attention to NULL values, operator precedence, parentheses, JOIN interactions, and database-specific behavior. Format complex conditions clearly and test important filters before using them in UPDATE or DELETE statements.