skip to content

Why must a derived table in the FROM clause be given an alias?

level: juniorimportance: should knowfreq 55%

answer

  1. it is a table like any other
  2. the outer query needs a name to qualify
  3. PostgreSQL and MySQL both refuse it
  4. correlation name, optionally with a column list

basics

~20 s

A derived table is a table reference in the FROM clause, and every table there needs a name so its columns can be qualified unambiguously. The standard requires that correlation name, and PostgreSQL and MySQL reject the query without it.

solid answer

~50 s

A subquery in `FROM` is a *derived table*: for the rest of the statement it is simply another table, so it needs a name like any other table reference. Without one there is no way to write `t.col`, and no way to disambiguate a column name that also exists in another FROM item. The standard mandates the correlation name; PostgreSQL answers `subquery in FROM must have an alias`, MySQL answers `Every derived table must have its own alias`. Not every engine enforces it, so a query that runs on one can fail on another — always write the alias. The standard also lets you rename the columns at the same time, as in `FROM (SELECT a, b FROM t) AS d(x, y)`. Give expression columns an explicit alias inside the subquery, because otherwise their name is implementation-dependent.

go deeper

for a junior

Recognise the error message and fix it instinctively: a subquery in FROM is a table, so give it a name and qualify its columns through that name.

for a middle

Explain what the name is for — qualification, disambiguation, and self-joins — and know the optional column-list form of the alias plus the need to alias expression columns inside the subquery.

for a senior

Point out that leniency in one engine is a portability trap, and enforce naming as a review rule so that column references stay unambiguous when someone later adds a join to the same query.

for a principal

Set the convention that makes generated and hand-written SQL interchangeable: stable outward column names on every derived table, so downstream queries and tooling do not depend on engine-invented identifiers.

## The derived table is a table When you write a parenthesised `SELECT` in the `FROM` clause, the result is a *derived table* — a table that exists only for the duration of this statement. The key mental move is that the outer query does not see a subquery there at all: it sees a table, with rows and columns, sitting in the FROM list next to whatever other tables you listed. Everything that is true of a real table reference is therefore true of it, and the first of those things is that it needs a name. ```sql -- Rejected on the strict engines: the derived table has no name SELECT * FROM (SELECT id, total FROM orders WHERE total > 100); -- Valid SELECT big.id, big.total FROM (SELECT id, total FROM orders WHERE total > 100) AS big; ``` ## Why the name is needed at all Three jobs depend on it. **Qualification.** `big.total` is only writable because `big` exists. Without a name there is no prefix you could use. **Disambiguation.** As soon as the derived table joins something else that has an `id` column, the parser needs to know which `id` you mean. Table names resolve that; a nameless table cannot. **Self-joins and repetition.** If the same derived table appears twice in one FROM list, only distinct correlation names keep the two apart. The SQL standard therefore mandates a correlation name on a derived table. PostgreSQL enforces it with `ERROR: subquery in FROM must have an alias`, and MySQL with `Every derived table must have its own alias`. Some engines are lenient and accept the omission, which is exactly why relying on it is a portability trap: the query is correct nowhere in the standard and happens to work in one place. ## The optional column list The alias can rename the columns too. Standard SQL, and PostgreSQL among others, accept a column list after the correlation name: ```sql SELECT d.customer, d.spend FROM (SELECT customer_id, SUM(total) FROM orders GROUP BY customer_id) AS d(customer, spend); ``` The list must have one entry per output column of the subquery. This form is handy when the inner query is generated or otherwise inconvenient to edit, because it fixes the outward-facing names without touching the body. ## Name your expressions inside The alias names the *table*, not its columns. A column produced by an expression has no natural name, and what an engine invents for it is implementation-dependent — PostgreSQL will show something like `?column?`, others pick something else. Any outer reference to such a column is therefore unreliable unless you name it yourself: ```sql SELECT line.order_id, line.line_total FROM ( SELECT order_id, price * quantity AS line_total FROM order_items ) AS line WHERE line.line_total > 50; ``` ## What the alias hides A point candidates trip over: once the subquery is named, the tables *inside* it are invisible from outside. In `FROM (SELECT id FROM orders) AS d`, the outer query may write `d.id` but not `orders.id` — the name `orders` does not exist at the outer level, and referencing it either fails or, worse, silently resolves to a different `orders` you also listed. The derived table is a boundary: what crosses it is exactly the columns of its select list, under the names that select list gave them. ## `AS` is optional; the name is not In standard SQL and in the major engines, the `AS` keyword before a table alias may be omitted: `FROM (SELECT …) d` and `FROM (SELECT …) AS d` mean the same thing. Many teams require `AS` anyway because it makes the alias visible at a glance and prevents the classic missing-comma bug in a select list, where a forgotten comma turns the next column name into an alias for the previous expression. The keyword is style; the name itself is a rule.

  • Is the AS keyword required, or can the alias be written bare?
    `AS` is optional before a table alias in standard SQL and in the major engines, so `FROM (SELECT …) d` and `FROM (SELECT …) AS d` are equivalent. Many teams require it for readability. In a select list `AS` is likewise optional, and omitting it is a classic source of an accidentally renamed column when a comma is missing.
  • What name does an unaliased expression column get inside a derived table?
    It is implementation-dependent — an engine may invent something like `?column?` — and you cannot rely on it. If the outer query needs that column, alias the expression inside the subquery. The optional column list on the derived table itself, as in `AS d(x, y)`, is the other portable way to fix names.

saying these in an interview costs you the question

  • Says the alias is optional because their engine allows it
  • Thinks the table alias also renames the subquery's columns
  • Believes the outer query can still see the inner table names
  • Expects a computed column to keep a predictable generated name
  • Confuses the derived-table alias with a column alias

context