Chapter 111: Selecting and Querying Data
The SELECT statement is the heart of SQL. It retrieves and shapes data from one or more tables and can include expressions, filters, sorting, and limits. Every analytic, report, and application query builds on its structure. This chapter covers its syntax, derived columns, and key modifiers for distinctness, limits, and ordering.
SELECT structure and clause order
The typical SELECT query has a predictable layout. Not every clause is required, but the order is fixed by the SQL standard:
SELECT [DISTINCT] column_list
FROM table_name
[JOIN ...]
WHERE condition
GROUP BY grouping_columns
HAVING group_condition
ORDER BY sort_columns
LIMIT number OFFSET start;
Logical evaluation order
Although you write SELECT first, the database engine logically processes the clauses in this sequence:
| Step | Clause | Description |
| 1 | FROM / JOIN |
Identify source tables and combine them. |
| 2 | WHERE |
Filter rows before grouping. |
| 3 | GROUP BY |
Aggregate rows into groups. |
| 4 | HAVING |
Filter groups based on aggregate conditions. |
| 5 | SELECT |
Produce output columns and expressions. |
| 6 | ORDER BY |
Sort the result set. |
| 7 | LIMIT / OFFSET |
Return a subset of rows. |
WHERE clause because WHERE is evaluated before SELECT. Use the full expression instead, or repeat it in both places if necessary.
Basic example
SELECT first_name, last_name, department, salary
FROM employees
WHERE salary > 80000
ORDER BY department, salary DESC;
* in production queries. It avoids surprises when new columns are added and reduces I/O.
Column aliases, expressions, and derived columns
Aliases rename columns in the output or simplify references to computed expressions. Use AS for clarity, though it is optional in most engines.
Using aliases
SELECT
first_name AS given,
last_name AS family,
salary * 1.1 AS adjusted_salary
FROM employees;
Aliased columns can make results easier to read and can be reused in the ORDER BY clause.
SELECT
id,
department_id,
salary * 1.1 AS adjusted_salary
FROM employees
ORDER BY adjusted_salary DESC;
Expressions and derived data
SQL expressions allow arithmetic, string concatenation, conditional logic, and type conversion directly in the query.
SELECT
first_name || ' ' || last_name AS full_name,
EXTRACT(YEAR FROM hire_date) AS hire_year,
CASE
WHEN salary >= 100000 THEN 'High'
WHEN salary >= 70000 THEN 'Medium'
ELSE 'Low'
END AS pay_band
FROM employees;
- Use arithmetic for numeric calculations (
price * quantity). - Use functions for transformations (
UPPER(name),SUBSTR(email, 1, 10)). - Use
CASEfor conditional derivations.
Table aliases and joins
Short aliases for tables make multi-table queries readable. Use clear, short forms like e for employees.
SELECT e.first_name, e.last_name, d.name AS department
FROM employees e
JOIN departments d ON e.department_id = d.id;
You can even reuse derived data in subqueries or common table expressions (CTEs).
WITH high_earners AS (
SELECT id, first_name, last_name, salary
FROM employees
WHERE salary > 100000
)
SELECT first_name, last_name
FROM high_earners
ORDER BY last_name;
The following modifiers control result uniqueness, size, and ordering. Their use and syntax vary slightly between engines.
DISTINCT
DISTINCT removes duplicate rows from the result set. It applies to the combination of all selected columns.
SELECT DISTINCT department
FROM employees;
SELECT DISTINCT department, role
FROM employees;
DISTINCT can be expensive on large datasets. If possible, filter or aggregate before applying it.
TOP, LIMIT, and OFFSET
These clauses restrict the number of rows returned. Syntax differs among engines.
| Engine | Keyword | Example |
| PostgreSQL / MySQL / SQLite | LIMIT / OFFSET |
SELECT * FROM employees ORDER BY salary DESC LIMIT 10 OFFSET 20; |
| SQL Server | TOP or OFFSET FETCH |
SELECT TOP (10) * FROM employees ORDER BY salary DESC; |
| Oracle | FETCH FIRST |
SELECT * FROM employees ORDER BY salary DESC FETCH FIRST 10 ROWS ONLY; |
-- PostgreSQL / MySQL
SELECT first_name, last_name, salary
FROM employees
ORDER BY salary DESC
LIMIT 5;
-- SQL Server
SELECT TOP (5) first_name, last_name, salary
FROM employees
ORDER BY salary DESC;
LIMIT with OFFSET for pagination, but use keyset pagination (for example, “where id > last_seen_id”) for large tables to avoid scanning overhead.
ORDER BY
ORDER BY defines the sort order of the result set. Without it, row order is not guaranteed, even if it appears consistent in testing.
SELECT first_name, last_name, salary
FROM employees
ORDER BY salary DESC, last_name ASC;
- Specify ascending (
ASC) or descending (DESC) per column. - Use column positions (
ORDER BY 3 DESC) only for quick tests, not production code. - Sort by expressions or aliases (
ORDER BY adjusted_salary DESC).
ORDER BY, SQL does not define any particular row order. Engines may return rows differently between executions.
By mastering SELECT structure, aliases, and output controls such as DISTINCT, LIMIT, and ORDER BY, you can build efficient, readable queries that form the backbone of all SQL usage.