WHERE clauses, how to summarise data with aggregate functions, and how to combine tables with joins.
11.1 Sample Tables We’ll Use
Every example in this chapter works on these two tables. Re-create them once and try the queries along with the text.
11.2 The WHERE Clause — Filtering Rows
A SELECT without WHERE returns every row. WHERE keeps only the rows whose condition evaluates to TRUE.
11.2.1 Comparison operators
| Operator | Meaning |
|---|---|
= | Equal to |
<> or != | Not equal to |
> < >= <= | Greater / less than (or equal) |
11.2.2 Logical operators — AND, OR, NOT
11.2.3 BETWEEN … AND …
Selects values inside an inclusive range. The two endpoints are included.
11.2.4 IN — match against a list
11.2.5 LIKE — pattern matching with wildcards
| Wildcard | Matches |
|---|---|
% | Any sequence of characters (0 or more) |
_ | Exactly one character |
11.2.6 IS NULL / IS NOT NULL
NULL means “unknown” / “missing” — it is not the same as 0 or an empty string. Never use = NULL; always use IS NULL.
11.3 Sorting — ORDER BY
11.3.1 LIMIT — first n rows
11.4 Aggregate Functions — Summarising a Column
| Function | Returns | NULL behaviour |
|---|---|---|
COUNT(*) | Number of rows | NULLs counted |
COUNT(col) | Number of non-NULL values in col | NULLs ignored |
COUNT(DISTINCT col) | Distinct non-NULL values | NULLs ignored |
SUM(col) | Total of numeric column | NULLs ignored |
AVG(col) | Average of numeric column | NULLs ignored |
MAX(col) | Largest value | NULLs ignored |
MIN(col) | Smallest value | NULLs ignored |
COUNT(*) counts every row including rows with NULLs; COUNT(column) only counts rows where that column is not NULL. Pick the right one for the question.
11.5 GROUP BY — Aggregate per Category
GROUP BY divides the rows into groups having the same value in the named column(s), and the aggregate is computed for each group.
SELECT list must either appear in GROUP BY or be wrapped in an aggregate function.
11.6 HAVING — Filtering on Aggregates
WHERE filters individual rows before grouping. HAVING filters groups after aggregation.
11.6.1 WHERE vs HAVING
WHERE | HAVING | |
|---|---|---|
| Operates on | Rows | Groups |
| Runs | Before GROUP BY | After GROUP BY |
| Can use aggregate? | ❌ No | ✅ Yes |
11.7 The Full SELECT — Clause Order
When you combine everything, the clauses must appear in this exact order:
(The numbers show logical execution order, not writing order.)
11.8 Joins — Combining Two Tables
11.8.1 Cartesian Product (cross join)
If you list two tables in the FROM clause with no condition, every row of the first table is paired with every row of the second — the Cartesian product. For tables of size m and n, the result has m × n rows.
11.8.2 Equi-join — matching on equality
Add a WHERE condition that matches the linking columns. This is the classic CBSE form.
11.8.3 Natural Join
A NATURAL JOIN automatically joins on columns that share the same name in both tables — here both tables have dept_id.
11.8.4 Modern INNER JOIN … ON … syntax
This form is recommended — the join condition is visible at the top of the query instead of buried in WHERE.
11.8.5 Summary of the three forms
| Form | Syntax | Notes |
|---|---|---|
| Cartesian | FROM a, b | No condition → m × n rows |
| Equi-join | FROM a, b WHERE a.x = b.x | Manual equality condition |
| Natural join | FROM a NATURAL JOIN b | Auto-matches columns with the same name |
| Inner join | FROM a INNER JOIN b ON a.x = b.x | Modern / recommended |
11.9 CBSE-style Worked Queries
11.9.1 Names of students scoring above the class average
11.9.2 City-wise topper list
11.9.3 Department with the most students
11.9.4 Names containing the letter ‘h’
11.9.5 Students whose name has exactly five letters
11.9.6 Students without a department and without a city
11.10 Common Mistakes to Avoid
| # | Mistake | Fix |
|---|---|---|
| 1 | Using = NULL instead of IS NULL | NULL never equals anything — always IS NULL / IS NOT NULL |
| 2 | WHERE with an aggregate — WHERE AVG(marks) > 80 | Aggregates belong in HAVING, not WHERE |
| 3 | Naked column in SELECT with GROUP BY | It must be in GROUP BY or wrapped in an aggregate |
| 4 | Writing ORDER BY before WHERE | The correct clause order is SELECT / FROM / WHERE / GROUP BY / HAVING / ORDER BY / LIMIT |
| 5 | Joining without a condition (Cartesian) | Always add ON … or WHERE a.x = b.x |
| 6 | Using % wildcard but forgetting LIKE operator | % only works in LIKE patterns |
| 7 | Mixing up _ and % wildcards | _ = exactly one char; % = any sequence |
| 8 | Expecting BETWEEN 70 AND 90 to exclude endpoints | It is inclusive — both 70 and 90 are matched |
Quick-revision summary
WHEREfilters rows — operators:= < > <= >= <>,AND / OR / NOT,BETWEEN,IN,LIKE(%+_),IS NULL.ORDER BY col [ASC|DESC];LIMIT nfor the first n rows.- Aggregate functions:
COUNT,SUM,AVG,MIN,MAX(NULLs ignored except inCOUNT(*)). GROUP BYpartitions rows;HAVINGfilters the resulting groups.- Clause order:
SELECT / FROM / WHERE / GROUP BY / HAVING / ORDER BY / LIMIT. - Joins — Cartesian (no condition), equi-join (manual
WHERE a.x = b.x), natural join (auto-matches same-name columns), INNER JOIN … ON … (modern, recommended).