What is a derived table, and what can you do with one that you cannot do with a plain subquery?
A derived table is a SELECT statement in the FROM clause, given a name with an alias. The engine treats it as a table for the rest of the query: you can select from it, join to it, group by its columns and reference its column aliases in the outer query.
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
amount DECIMAL(10, 2) NOT NULL
);
INSERT INTO orders (id, customer_id, amount) VALUES
(1, 1, 120.00),
(2, 1, 340.50),
(3, 2, 75.25),
(4, 2, 15.00);
SELECT t.customer_id, t.order_count, t.total
FROM (
SELECT customer_id,
COUNT(*) AS order_count,
SUM(amount) AS total
FROM orders
GROUP BY customer_id
) AS t
WHERE t.order_count > 1
ORDER BY t.customer_id;One row comes back: customer 1 with 2 orders totalling 460.50. Customer 2 has one order, so the outer filter removes that group.
The alias is mandatory — a derived table without one is a syntax error in every engine — and it is what the outer query refers to. The derived table's own aliases, order_count and total, become ordinary column names in the outer query.
What the derived table buys you is a staging point. Because the outer query sees a flat set of columns, you can filter on the aggregate, join the result to something else, and group by a computed value. The biggest of these is grouping by an expression: the outer query cannot GROUP BY a CASE expression directly in most engines without repeating it, but it can group by a column that a derived table computed for it.
One important difference from a CTE. A derived table is scoped to the single query it appears in and cannot be referenced twice. A CTE can be referenced multiple times, which is why the CTE is the better tool when you need the same roll-up in two places. A derived table is the better tool when you want the optimizer to treat the intermediate result as part of the join tree, because the planner is free to push predicates into it.