The WHERE clause is used to filter rows based on a specified condition.
It helps retrieve only the records that satisfy a given requirement before any grouping or aggregation occurs.
SELECT column_name
FROM table_name
WHERE condition;
WHERE filters rows using a predicate — a condition that resolves to TRUE, FALSE, or UNKNOWN for each row. Only rows where the predicate is TRUE survive. NULL comparisons resolve to UNKNOWN, not FALSE, which is why = NULL silently matches nothing — see Common Mistakes below.
flowchart LR
A[FROM: all rows loaded] --> B{WHERE: predicate per row}
B -->|TRUE| C[row kept]
B -->|FALSE or UNKNOWN| D[row dropped]
style B fill:#2f6feb,color:#ffffff
Standard comparisons (=, <>, IS NULL, IS NOT NULL, AND/OR) behave identically across MySQL, PostgreSQL, SQL Server, and Oracle. Where engines diverge is null-safe equality — comparing two columns that might both be NULL and treating NULL = NULL as a match:
| Engine | Null-safe equality operator |
|---|---|
| MySQL | <=> (e.g. a <=> b) |
| PostgreSQL | IS NOT DISTINCT FROM (e.g. a IS NOT DISTINCT FROM b) |
| SQL Server | No dedicated operator — requires (a = b OR (a IS NULL AND b IS NULL)) |
| Oracle | DECODE(a, b, 1, 0) = 1 or the same OR-based pattern as SQL Server |
IS NOT DISTINCT FROM (PostgreSQL) is the closest to an ANSI-standard pattern, though not universally implemented.
| Column | Description |
|---|---|
| emp_id | Employee ID |
| emp_name | Employee Name |
| dept_id | Department ID |
| manager_id | Reporting Manager |
| emp_id | emp_name | dept_id | manager_id |
|---|---|---|---|
| 1 | Ammar | 1 | 3 |
| 2 | Riya | 2 | 3 |
| 3 | Sahil | 1 | NULL |
| 4 | Priya | 3 | 2 |
| 5 | Arjun | 2 | 1 |
SELECT *
FROM employes
WHERE dept_id = 1;
| emp_name |
|---|
| Ammar |
| Sahil |
SELECT *
FROM employes
WHERE manager_id IS NOT NULL;
SELECT *
FROM employes
WHERE manager_id IS NULL;
| emp_name |
|---|
| Sahil |
SQL processes queries in the following order:
Because WHERE runs before GROUP BY, it filters rows before aggregation occurs.
SELECT *
FROM employes
WHERE manager_id = NULL;
= NULL never matches anything, even rows where manager_id genuinely is NULL — NULL is not a value you can compare with =, it represents the absence of a value.
SELECT *
FROM employes
WHERE manager_id IS NULL;
SELECT dept_id, COUNT(*)
FROM employes
WHERE COUNT(*) > 1
GROUP BY dept_id;
WHERE cannot use aggregate functions because it executes before GROUP BY produces any groups to aggregate.
SELECT dept_id, COUNT(*)
FROM employes
GROUP BY dept_id
HAVING COUNT(*) > 1;
NOT IN with a NULL in the list — WHERE dept_id NOT IN (1, NULL) returns zero rows, even for departments that are clearly not 1. Once any value in a NOT IN list is NULL, the whole predicate evaluates to UNKNOWN for every row. Prefer NOT EXISTS (covered in the subqueries module) whenever the list comes from a subquery that might contain NULL.WHERE emp_name = 'ammar' may or may not match 'Ammar' depending on the column’s collation setting, which is a database configuration choice, not a SQL-language rule. Don’t assume case sensitivity behavior transfers between environments.A very common SQL interview question:
| WHERE | HAVING |
|---|---|
| Filters rows | Filters groups |
| Executes before GROUP BY | Executes after GROUP BY |
| Cannot use aggregate functions | Can use aggregate functions |
Remember:
WHERE → Rows
HAVING → Groups
emp_id, then filter manager_id against it — this can be done with a subquery.)04_Joins)Questions 6 and 7 need the
departmentstable (see README.md → Datasets) joined toemployes, which this module doesn’t cover yet. Attempt them after completing the joins module, then come back and revisit — it’s a useful way to confirm the concept actually stuck.
GLOSSARY.md — definitions of “predicate” and other terms used aboveFAQ.md — recurring questionsINTERVIEW_PREP.md — see “= NULL vs IS NULL” and “WHERE vs HAVING”