MetricSign
Start free
Medium severityquery

MySQL Error:
1248

What does this error mean?

MySQL error 1248 is thrown at parse time when a subquery in the FROM clause lacks an alias. MySQL treats every subquery in FROM as a derived table — a temporary result set that must be addressable by name in the outer query. Without an alias, the query planner cannot reference the result set and rejects the statement immediately with 'Every derived table must have its own alias'. In a data pipeline context this means an ADF Copy or Lookup activity, a dbt model, or an Airflow task running a MySQL source query fails at the first execution attempt — no rows are transferred and downstream tables never receive new data.

Common causes

  • 1Subquery in FROM clause without an AS clause: `SELECT * FROM (SELECT id, created_at FROM orders WHERE status = 'open')` — MySQL rejects this immediately at parse time before touching any data.
  • 2SQL ported from SQL Server or PostgreSQL where derived table aliases are optional syntax: those engines accept anonymous subqueries in FROM; MySQL does not and will error on every such query.
  • 3BI tool or ORM auto-generated SQL that omits the alias. Power BI DirectQuery, Tableau, and older versions of SQLAlchemy have all been known to generate alias-free derived tables against MySQL sources.
  • 4Nested subqueries where only the outermost level has an alias: `SELECT * FROM (SELECT * FROM (SELECT id FROM t) AS inner_q)` — the outer subquery is still missing its alias and MySQL errors on that level.
  • 5dbt macros or Jinja-templated SQL that conditionally builds a derived table but forgets to append the alias token when the branch is taken.
  • 6UNION or INTERSECT wrapped in a subquery without an alias: `SELECT * FROM (SELECT a FROM t1 UNION SELECT a FROM t2)` — the UNION result is itself a derived table and requires an alias.
  • 7Hand-written migration scripts that concatenate SQL strings dynamically and drop the alias in the string join logic, causing intermittent failures depending on runtime parameter values.

How to fix it

  1. 1Step 1: Run the failing query directly in a MySQL client to confirm error 1248 and locate the exact subquery. The error message does not include a line number, so scan every FROM clause: `mysql -u user -p -e "EXPLAIN SELECT ..."` — EXPLAIN also triggers the parser and will surface the error.
  2. 2Step 2: Add AS alias_name immediately after the closing parenthesis of each subquery in the FROM clause: `SELECT * FROM (SELECT id, created_at FROM orders WHERE status = 'open') AS open_orders;`
  3. 3Step 3: For nested subqueries, alias every level independently: `SELECT outer_q.id FROM (SELECT inner_q.id FROM (SELECT id FROM orders) AS inner_q WHERE inner_q.id > 100) AS outer_q;`
  4. 4Step 4: If the query originates from a BI tool, check the tool's MySQL dialect setting. In Tableau: Data → Edit Connection → set database dialect to MySQL. In Power BI: change the native query in Power Query Editor and add the alias manually in Advanced Editor.
  5. 5Step 5: For MySQL 8.0+, refactor deeply nested derived tables into CTEs to eliminate the aliasing problem at the structural level: `WITH open_orders AS (SELECT id, created_at FROM orders WHERE status = 'open'), recent AS (SELECT id FROM open_orders WHERE created_at > NOW() - INTERVAL 7 DAY) SELECT * FROM recent;` — CTEs are referenced by their WITH-clause name and never need a separate AS alias.
  6. 6Step 6: In dbt, if the error occurs during a model run (`dbt run --select model_name`), inspect the compiled SQL in `target/compiled/` to see the exact query MySQL receives. Edit the source model or macro to include the alias, then rerun.
  7. 7Step 7: In ADF, open the pipeline → select the failing Copy or Lookup activity → Source tab → paste the corrected query into the Query field. Trigger a debug run to verify the fix before publishing.

Example log output

ERROR 1248 (42000): Every derived table must have its own alias
[ADF] Activity 'LookupOrderSummary' failed: ErrorCode=UserErrorOdbcOperationFailed, Message=ERROR [42000] [MySQL][ODBC 8.0(w) Driver][mysqld-8.0.32]Every derived table must have its own alias

Frequently asked questions

Why does MySQL require aliases on derived tables when SQL Server does not?

MySQL's SQL standard interpretation requires that every derived table — any subquery in a FROM clause — must be named so the outer query can reference it unambiguously. SQL Server and PostgreSQL allow anonymous derived tables as an extension; MySQL does not implement that extension. When porting SQL across databases, derived table aliases must be added explicitly.

Will adding a retry in ADF fix MySQL error 1248?

No. Error 1248 is a parse-time syntax error — MySQL rejects the query before it touches any data. Retrying will produce the same failure every time. The only fix is correcting the SQL by adding the missing alias. Set the ADF activity retry count to 0 for syntax errors to avoid wasting pipeline run time.

Do CTEs in MySQL 8.0 need an alias like derived tables do?

No — a CTE defined in a WITH clause is referenced by the name given in the WITH clause itself (`WITH cte_name AS (...)`). There is no additional AS alias required when you SELECT FROM the CTE. This is one reason CTEs are preferred over nested derived tables: the naming is explicit at the point of definition, not at the point of use.

How do I find which subquery is missing the alias in a long generated SQL statement?

Run `EXPLAIN <query>` in a MySQL client — the parser will reject it at the same point as a normal execution and print the 1248 error. Then search the query text for every occurrence of `)` followed by a keyword other than AS — each such location is a derived table without an alias. For ADF queries, copy the SQL from the activity Source tab into a MySQL Workbench session to inspect it directly.

Source · dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html

Other query errors