Lesson 3 of 30
SELECT, WHERE, ORDER BY, and LIMIT
SELECT reads rows. Every query is built from a few clauses, and they run in a fixed logical order that explains most beginner errors.
| Order | Clause | Purpose |
|---|---|---|
| 1 | FROM |
pick the table |
| 2 | WHERE |
keep rows that match |
| 3 | GROUP BY |
group rows |
| 4 | HAVING |
keep groups that match |
| 5 | SELECT |
compute the output columns |
| 6 | DISTINCT |
remove duplicate rows |
| 7 | ORDER BY |
sort |
| 8 | LIMIT |
cut the result |
You write SELECT first, but it runs after WHERE. This is why a column alias defined in SELECT cannot be used in WHERE.
Example 1: choose columns
SELECT name, salary FROM employees; name | salary
--------+--------
Meera | 250000
Ravi | 180000
Kiran | 150000
Neha | 150000
Arjun | 110000
Divya | 160000
Sameer | 90000
Pooja | 85000
Vikram | 120000
Anita | 80000
Rohan | 95000
Tara | 70000
(12 rows)Avoid SELECT * in application code: it returns columns you do not need and breaks when columns are added.
Example 2: filter with WHERE
SELECT name, salary
FROM employees
WHERE salary > 150000; name | salary
-------+--------
Meera | 250000
Ravi | 180000
Divya | 160000
(3 rows)Example 3: combine conditions
AND binds tighter than OR. Use parentheses to say what you mean.
SELECT name, dept_id, salary
FROM employees
WHERE dept_id = 1 AND (salary >= 180000 OR hired_on >= '2021-01-01'); name | dept_id | salary
-------+---------+--------
Meera | 1 | 250000
Ravi | 1 | 180000
Arjun | 1 | 110000
(3 rows)Example 4: IN and BETWEEN
BETWEEN includes both ends.
SELECT name, salary
FROM employees
WHERE dept_id IN (2, 3)
AND salary BETWEEN 85000 AND 120000; name | salary
--------+--------
Sameer | 90000
Pooja | 85000
Vikram | 120000
(3 rows)Example 5: pattern matching with LIKE
% matches any run of characters, _ matches exactly one. ILIKE (PostgreSQL) ignores case.
SELECT name FROM employees WHERE name LIKE '%a'; name
-------
Meera
Neha
Divya
Pooja
Anita
Tara
(6 rows)SELECT name FROM employees WHERE name LIKE '_a%'; name
--------
Ravi
Sameer
Tara
(3 rows)Example 6: sort with ORDER BY
Without ORDER BY, row order is not guaranteed, even if it looks stable. Sort by several columns, each with its own direction.
SELECT name, dept_id, salary
FROM employees
WHERE dept_id = 1
ORDER BY salary DESC, name ASC; name | dept_id | salary
-------+---------+--------
Meera | 1 | 250000
Ravi | 1 | 180000
Kiran | 1 | 150000
Neha | 1 | 150000
Arjun | 1 | 110000
(5 rows)Example 7: LIMIT and OFFSET
SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3; name | salary
-------+--------
Meera | 250000
Ravi | 180000
Divya | 160000
(3 rows)OFFSET skips rows, which gives page 2 of a sorted list. Large offsets are slow because the database still reads and discards the skipped rows. See fixing slow queries for keyset pagination.
SELECT name, salary
FROM employees
ORDER BY salary DESC, id
LIMIT 3 OFFSET 3; name | salary
--------+--------
Kiran | 150000
Neha | 150000
Vikram | 120000
(3 rows)Example 8: DISTINCT
DISTINCT removes duplicate rows from the result.
SELECT DISTINCT status FROM orders ORDER BY status; status
-----------
cancelled
delivered
placed
shipped
(4 rows)SELECT DISTINCT city, status
FROM customers c
JOIN orders o ON o.customer_id = c.id
ORDER BY city, status; city | status
-----------+-----------
Bengaluru | delivered
Bengaluru | placed
Bengaluru | shipped
Delhi | delivered
Mumbai | cancelled
Mumbai | delivered
Pune | placed
(7 rows)Example 9: expressions and aliases
Columns can be computed. AS gives the result column a name.
SELECT name,
salary,
salary / 12 AS monthly,
salary * 0.10 AS bonus
FROM employees
WHERE id <= 3; name | salary | monthly | bonus
-------+--------+---------+----------
Meera | 250000 | 20833 | 25000.00
Ravi | 180000 | 15000 | 18000.00
Kiran | 150000 | 12500 | 15000.00
(3 rows)salary / 12 is integer division because both sides are integers. Use salary / 12.0 if you need decimals.
Example 10: you cannot use an alias in WHERE
WHERE runs before SELECT, so the alias does not exist yet.
SELECT name, salary / 12 AS monthly
FROM employees
WHERE monthly > 10000;ERROR: column "monthly" does not exist
LINE 3: WHERE monthly > 10000;
^Repeat the expression, or wrap the query in a subquery.
SELECT name, monthly
FROM (SELECT name, salary / 12 AS monthly FROM employees) e
WHERE monthly > 10000; name | monthly
-------+---------
Meera | 20833
Ravi | 15000
Kiran | 12500
Neha | 12500
Divya | 13333
(5 rows)ORDER BY runs after SELECT, so it can use the alias.
Summary
- Clauses run as
FROM,WHERE,GROUP BY,HAVING,SELECT,ORDER BY,LIMIT. - Use parentheses whenever
ANDandORare mixed. - Add a tie-breaker column to
ORDER BYwhen paging so pages are stable.
