Schema Design Basics
9 examples to get you started with schema design - 6 basic and 3 intermediate.
Search across all documentation pages
9 examples to get you started with schema design - 6 basic and 3 intermediate.
CREATE on a development database.
# Local dev with Docker (PostgreSQL 18)
docker run -d --name pg18 -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:18
psql postgres://postgres:dev@localhost:5432/postgresOpaque id generated by database.
CREATE TABLE app.accounts (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL
);Business identifier with UNIQUE constraint.
CREATE TABLE app.accounts (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
external_ref text NOT NULL UNIQUE,
name text NOT NULL
);Plural nouns and schema prefix.
CREATE TABLE app.order_lines (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
order_id bigint NOT NULL,
sku text NOT NULL,
qty int NOT NULL CHECK (qty > 0)
);Name child column parent_table_id.
ALTER TABLE app.order_lines
ADD CONSTRAINT order_lines_order_id_fkey
FOREIGN KEY (order_id) REFERENCES app.orders(id);Reduce NULL ambiguity.
CREATE TABLE app.settings (
account_id bigint PRIMARY KEY REFERENCES app.accounts(id),
theme text NOT NULL DEFAULT 'light'
);Database-enforced row rules.
ALTER TABLE app.order_lines
ADD CONSTRAINT order_lines_qty_positive CHECK (qty > 0);When business pair is unique.
CREATE TABLE app.region_codes (
country char(2) NOT NULL,
region_code text NOT NULL,
name text NOT NULL,
PRIMARY KEY (country, region_code)
);deleted_at marks inactive rows.
ALTER TABLE app.accounts ADD COLUMN deleted_at timestamptz;
CREATE INDEX accounts_active_idx ON app.accounts (id) WHERE deleted_at IS NULL;Catalog documentation.
COMMENT ON TABLE app.accounts IS 'Tenant billing account';
COMMENT ON COLUMN app.accounts.external_ref IS 'CRM id';Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.
Reviewed by Chris St. John·Last updated Jul 16, 2026