CREATE TABLE
CREATE TABLE defines a new table: its name, its columns, each column's data type, and any constraints that should be enforced on the data going in. This single statement is where most of a schema's design decisions actually get written down.
Syntax
General shape of CREATE TABLE
CREATE TABLE table_name (
column1 data_type constraints,
column2 data_type constraints,
column3 data_type constraints,
...
table_level_constraint
);A complete worked example
Here is a realistic users table that mixes several column types with constraints defined inline, right next to the column they apply to.
A users table with columns, types, and constraints
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
full_name VARCHAR(150) NOT NULL,
age SMALLINT CHECK (age >= 0),
is_active BOOLEAN NOT NULL DEFAULT TRUE,
signup_date DATE NOT NULL DEFAULT CURRENT_DATE,
referred_by INTEGER REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);Reading this column by column: id is an auto-incrementing integer and the primary key. email is required and must be unique across all rows. full_name is required text. age is optional but, if provided, must be zero or greater. is_active defaults to TRUE if not specified. signup_date defaults to today's date. referred_by is a foreign key pointing back at another row in the same table. created_at records the exact moment the row was inserted, timezone-aware.
IF NOT EXISTS — safe to re-run
Running CREATE TABLE users (...) a second time against a database that already has a users table raises an error. Adding IF NOT EXISTS makes the statement a no-op instead of an error when the table is already there — handy for setup scripts and migrations that might be run more than once.
Idempotent table creation
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE
);
-- Running this twice in a row succeeds both timesCREATE TABLE ... AS SELECT
You can also create a new table directly from the result of a query. The new table's columns and data types are inferred from the query, and it is populated immediately with the query's rows. This is useful for snapshotting data, building a quick summary table, or staging a filtered subset of a larger table.
Creating a table from a SELECT
CREATE TABLE active_users AS SELECT id, email, full_name FROM users WHERE is_active = TRUE; -- active_users now exists as its own independent table with a snapshot -- of matching rows; it does not stay in sync with future changes to users.
Column order in the definition generally does not affect query behavior, but it does affect the default column order in
SELECT *and in some export tools.Table and column names are case-insensitive by default in most dialects unless quoted, but conventions vary — pick a convention (like snake_case) and stick to it.
CREATE TABLE ... AS SELECTcopies data, not constraints — the new table typically has no primary key, foreign keys, orNOT NULLconstraints unless you add them afterward.