Postgres by Example: CREATE TABLE
June 22, 2026 · View on GitHub
Tables are how PostgreSQL stores your data. CREATE TABLE defines a new table by listing its columns and their data types. Defaults, constraints, indexes, and foreign keys can all be added at create time, but a minimal table needs only a name and a column list. This lesson keeps things small; constraints get their own lessons.
What you'll learn:
- Defining a table with
CREATE TABLE - Picking a data type for each column (
integer,text, etc.) - Making the create script idempotent with
DROP TABLE IF EXISTS - Inspecting tables in the
publicschema - The difference between
TEMP TABLE, regular tables, andUNLOGGED TABLE
-- Drop the table if it exists so the script is idempotent
DROP TABLE IF EXISTS fruits;
-- A minimal table: id and name
CREATE TABLE fruits (
id integer,
name text
);
-- Confirm it exists in the current schema
SELECT schemaname, tablename
FROM pg_tables
WHERE tablename = 'fruits';
-- See the columns and their types
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'fruits'
ORDER BY ordinal_position;
fruits has two columns: id (a 32-bit signed integer) and name (variable-length text). The order of columns in CREATE TABLE is the physical order rows are stored in — for storage efficiency on wide tables, group fixed-width columns together. For small tables it doesn't matter.
DROP TABLE IF EXISTS removes the table only if it exists; without IF EXISTS, the statement would error on a fresh database. Combined with CREATE TABLE, you get a script you can rerun safely while you iterate on the schema. In production, of course, do not blindly drop tables — use migrations.
There are several variants of CREATE TABLE worth knowing about:
CREATE TEMP TABLE(orCREATE TEMPORARY TABLE) creates a table that exists only for the current session and is dropped automatically.CREATE UNLOGGED TABLEskips the write-ahead log — faster writes, but the contents are wiped if the server crashes. Useful for derived/staging data you can rebuild.CREATE TABLE ... (LIKE other_table INCLUDING ALL)clones the schema of another table.CREATE TABLE ... AS SELECT ...creates and populates a table from a query (called a CTAS).
To run:
$ psql -f source/create-table.sql postgres
DROP TABLE
CREATE TABLE
schemaname | tablename
------------+-----------
public | fruits
(1 row)
column_name | data_type | is_nullable
-------------+-----------+-------------
id | integer | YES
name | text | YES
(2 rows)
Common pitfalls:
- Picking
varchar(255)because that's what you learned in MySQL: in PostgreSQL,textandvarchar(n)have the same performance. Usetextunless you specifically want a length cap. - Forgetting
NOT NULLon columns that should never be null — you can add it later, but it is easier to set up correctly from the start. - Naming a column
user,order,select— these are reserved words. Either rename (user_id,placed_at) or quote ("user"), but the second is friction forever.
Tip: Standard column types worth memorizing: integer (32-bit), bigint (64-bit), text, boolean, date, timestamp with time zone (almost always preferred over timestamp), numeric(p,s) for exact decimals, and uuid for opaque IDs. Each gets its own lesson.
Try it: Add a third column, e.g. price numeric(10,2), and run the script again. Then change integer to bigint and rerun. Inspect with \d fruits in an interactive psql session.
Source: create-table.sql
Next: INSERT
Home: Postgres by Example