What does CREATE TABLE AS SELECT copy from the source query, and what does it leave behind?
answer
- it copies a result set, not a table
- column names come from the select list
- alias every computed column
- keys, indexes and defaults do not travel
- WITH NO DATA builds the empty shell
basics
~20 sCREATE TABLE AS SELECT builds a new table from a query result: column names and types are derived from the select list, and the returned rows are inserted. Keys, indexes, defaults and constraints are not carried over.
solid answer
~50 s`CREATE TABLE t2 AS SELECT ... FROM t1` creates a table whose shape is inferred from the **query result**, then fills it with the rows that query returned. The new column names are the select-list output names, so any expression needs an explicit alias or you get an engine-chosen or rejected name; the types are whatever the engine infers for those expressions. Everything that is a property of the source *table* rather than of the *result set* is lost: primary and foreign keys, unique and check constraints, indexes, column defaults, identity/auto-increment behaviour, triggers and comments. In PostgreSQL the new columns are not even `NOT NULL`. The standard offers `WITH DATA` and `WITH NO DATA` to choose whether rows are copied; not every engine accepts them, and the portable structure-only trick is a `WHERE 1 = 0` predicate. Treat CTAS as a fast way to materialise a result, never as a schema clone.
code
sql · 11 lines-- Fast, but the new table has no key, no indexes, no defaults
CREATE TABLE order_summary AS
SELECT customer_id,
COUNT(*) AS order_count,
CAST(SUM(net_amount) AS DECIMAL(12,2)) AS total_net
FROM orders
GROUP BY customer_id;
-- Empty shell only
CREATE TABLE order_summary_empty AS
SELECT * FROM order_summary WITH NO DATA;go deeper
Know that this one statement both creates the table and fills it with the query's rows, and that you name the new columns by aliasing the select list.
Explain that the shape comes from the result set, so keys, indexes, defaults and constraints are absent, and that types are inferred and may be widened.
Be able to say when a CTAS result is acceptable — staging, reporting, ad-hoc analysis — and when a real CREATE TABLE plus INSERT ... SELECT is required because the table will be written to or joined on.
Set the standard for how derived tables enter the schema: if a CTAS output survives past one job, it needs a checked-in DDL definition, or the schema slowly fills with tables nobody can recreate.
## The statement `CREATE TABLE ... AS SELECT`, universally shortened to CTAS, is a single statement that does two things: it defines a new table and it populates it. Crucially, it defines that table from a **query result**, not from a source table's definition. ```sql CREATE TABLE order_summary AS SELECT customer_id, COUNT(*) AS order_count, SUM(net_amount) AS total_net FROM orders GROUP BY customer_id; ``` The resulting table has three columns because the query produced three, and it holds one row per customer because the query did. ## Where the column names come from The new table's column names are the output column names of the select list. A plain column reference carries its own name across. An expression has no natural name, so it needs an explicit alias — `COUNT(*) AS order_count` above. Without the alias, some engines invent a placeholder name and others reject the statement outright, and either outcome makes the table awkward to query afterwards. Aliasing every computed column is not stylistic advice here; it is part of writing a correct CTAS. Most engines also let you override the names in the statement itself: ```sql CREATE TABLE order_summary (customer_id, order_count, total_net) AS SELECT customer_id, COUNT(*), SUM(net_amount) FROM orders GROUP BY customer_id; ``` ## Where the column types come from Types are inferred from the expressions. `customer_id` keeps the source column's type. `COUNT(*)` becomes whatever integer type the engine uses for counts. `SUM(net_amount)` becomes a widened numeric type, which may not equal the type of `net_amount`. Concatenations, `CASE` expressions and arithmetic all produce engine-determined types, and precision or length may differ from what you expected. If the resulting types matter — because another process reads the table, or because it feeds a later join — cast explicitly in the select list rather than trusting inference: ```sql SELECT CAST(SUM(net_amount) AS DECIMAL(12,2)) AS total_net ``` ## What is not copied This is the heart of the question. Because the input is a result set, anything that exists only as a property of the original table cannot travel: - primary keys and unique constraints - foreign keys, in either direction - `CHECK` constraints - indexes - column `DEFAULT` clauses - identity / auto-increment behaviour and the sequence behind it - triggers, comments and privileges In PostgreSQL the new columns do not even inherit `NOT NULL`. Engines differ in small details — some carry over more column attributes than others — so the safe mental model is: **CTAS gives you columns, types and rows, and you should assume nothing else.** ## WITH DATA and WITH NO DATA Standard SQL lets the statement say explicitly whether rows are copied: ```sql CREATE TABLE order_summary AS SELECT ... WITH NO DATA; ``` `WITH DATA` is the default meaning; `WITH NO DATA` creates the empty shell. PostgreSQL supports both. Not every engine accepts the clause, and where it is missing the portable equivalent is a predicate that matches nothing: ```sql CREATE TABLE order_summary AS SELECT ... FROM orders WHERE 1 = 0; ``` That produces the same empty, constraint-free shell. If what you actually want is a structural clone *with* the source's defaults and nullability, CTAS is the wrong tool — the `LIKE` table element exists for that. ## Dialect spellings The CTAS shape is widely available, but not universally spelled the same way. Microsoft SQL Server's traditional form is `SELECT ... INTO new_table FROM ...`, which is a `SELECT` statement that creates its target rather than a `CREATE TABLE` statement. Because the spelling varies, a migration script that must run on more than one engine is one of the few places where writing the `CREATE TABLE` and a separate `INSERT ... SELECT` is genuinely the more portable choice. ## When CTAS is the right tool CTAS shines where the output *is* a result set and nothing more is expected of it: materialising a heavy aggregate for a report, staging a transformed extract, capturing a quick working set during an investigation. It is a poor fit whenever the new table will be written to by an application, joined on heavily, or relied upon for integrity — in those cases write the real `CREATE TABLE` with its keys, defaults and constraints, then load it with `INSERT ... SELECT`.
- How do you control the column types in a CTAS result rather than accepting what the engine infers?Cast explicitly in the select list: `CAST(SUM(net_amount) AS DECIMAL(12,2)) AS total_net`. Aggregates and arithmetic widen types in engine-specific ways, so an inferred column may have different precision or length from the source. Where the resulting type is contractual — another job reads the table, or it joins to a typed column later — pin it with a cast instead of trusting inference.
- You need an empty table with exactly the source table's columns. Is CTAS the right tool?Only if you want columns and types alone. `CREATE TABLE t2 AS SELECT * FROM t1 WITH NO DATA` (or `WHERE 1 = 0`) gives a shell with no defaults, no keys, no indexes and, in PostgreSQL, no NOT NULL. When you want the definition rather than the result shape, use the `LIKE` table element, which copies the column definition and can optionally include defaults, constraints and indexes.
- Why must expressions in the select list of a CTAS be aliased?The new table's column names are the query's output column names. A bare column reference supplies one; an expression such as `COUNT(*)` or `a + b` does not. Some engines then invent a placeholder name that is painful to reference, and others reject the statement. An explicit `AS name` makes the resulting schema deliberate and readable.
saying these in an interview costs you the question
- Believes CTAS clones the primary key and indexes
- Thinks constraints and defaults come along with the rows
- Assumes column types exactly match the source columns
- Leaves computed columns unaliased and hopes for the best
- Uses CTAS to create an application table then adds nothing