Q1Which of the following is the correct order in which SQL clauses are evaluated?
Composing Queries — Combining JOIN, Subqueries, and UNION
Learn how to grow a single query step by step by combining JOIN, subqueries, and UNION with WHERE / ORDER BY / LIMIT, all running live in your browser.
Data used in this article — employee / department / sales
JOIN (connecting multiple tables), subqueries (a query inside a query), and UNION (stacking results vertically) each work on their own, but when you combine them with WHERE / ORDER BY / LIMIT you can write practical, real-world queries.
In this article you'll walk through, in order, how to combine these techniques and grow them into a single query.
The material is three employee tables — employee (30 employees), department (6 departments), and sales (50 sales line items).
You'll start with a simple JOIN, then build up through subqueries and UNION step by step.
Nail down the order clauses are evaluated — JOIN, WHERE, ORDER BY, LIMIT
The order you write a query in (SELECT … FROM … WHERE … ORDER BY) is different from the order it's evaluated in.
The database first connects tables with FROM / JOIN, then narrows rows with WHERE, aggregates with GROUP BY / HAVING, builds columns with SELECT, and finally sorts with ORDER BY and limits the row count with LIMIT.
Once you have this order down, rules like "to filter on an aggregated result use HAVING, not WHERE" and "LIMIT takes effect after sorting" start to make sense.
-- JOIN + WHERE + ORDER BY: employees in Osaka departments, ordered by hire date
SELECT emp.name, dept.dept_name, emp.hired_on
FROM employee emp
JOIN department dept ON dept.dept_id = emp.dept_id
WHERE dept.location = 'Osaka'
ORDER BY emp.hired_on;
Build a reference value with a subquery and narrow the JOIN result
A subquery can be used to build the "reference value" you need to further narrow down a result you assembled with JOIN.
A criteria value that changes per employee, like "the average salary of my own department," is computed with a correlated subquery (a subquery that's recalculated for each individual employee) that references the outer row.
When you attach the department name with JOIN and narrow rows using a correlated subquery condition, several pieces of syntax mesh together in one query.
-- JOIN + correlated subquery: employees whose salary equals their own department's max (the top earner in the department)
SELECT emp.name, dept.dept_name, emp.salary
FROM employee emp
JOIN department dept ON dept.dept_id = emp.dept_id
WHERE emp.salary = (
SELECT MAX(dept_member.salary)
FROM employee dept_member
WHERE dept_member.dept_id = emp.dept_id
);
Combine two perspectives with UNION and finish with ORDER BY
UNION stacks the results of two SELECTs vertically.
The upper and lower SELECTs must be union-compatible (the same number of columns, with the types of the corresponding columns being compatible).
UNION collapses fully identical rows into one (removing duplicates), while UNION ALL keeps duplicates as-is.
Sorting and row-count limits apply to the combined result as a whole, so write ORDER BY / LIMIT only once, at the very end.
-- UNION ALL: stack two perspectives vertically with a label column (keeps duplicates)
SELECT name, 'Kyoto' AS via FROM employee WHERE city = 'Kyoto'
UNION ALL
SELECT emp.name, 'HighSales' AS via
FROM employee emp
JOIN sales sale ON sale.emp_id = emp.emp_id
GROUP BY emp.emp_id
HAVING SUM(sale.amount) >= 1500000
ORDER BY name;
Knowledge Check
Answer each question one by one.
Q2Which is the correct explanation of WHERE emp.salary > (SELECT AVG(dept_member.salary) FROM employee dept_member WHERE dept_member.dept_id = emp.dept_id)?
Q3When you connect two SELECTs with UNION and sort the whole thing by name, which is the correct way to write ORDER BY?