postgresql-table-design
π―SkillHelps your AI assistant plan and review how data is stored in a PostgreSQL database. It gives clear rules for picking data types, keys, checks and search speed-ups so your tables stay correct and fast.
Installation
npx skills add wshobson/agents --skill postgresql-table-designRun it in a terminal; it asks which AI coding tools to install it for (Claude Code, Codex, Cursor and more).
Uses skills.sh Β· Node.js 22.20+
After installing
Ask for related work and the agent uses it on its own, or call it yourself.
/postgresql-table-designInstall by hand
Copy the whole skill folder from GitHub to the place below. Copying only SKILL.md can leave out files the skill uses.
Open the skill folder β- This project
.claude/skills/postgresql-table-design/ - All projects
~/.claude/skills/postgresql-table-design/
These paths are for Claude Code. For other tools, the command above puts it in the right place.
What this skill does
Overview
This skill guides an AI assistant when it designs or reviews tables in PostgreSQL, a popular open-source database. A schema is the plan for what tables and columns a database has. The skill gives firm rules and points to a longer reference file for deeper topics.
Key Features
- Pick the right column type for IDs, money, text and dates, with a list of types to avoid.
- Add keys and rules that stop bad data, such as missing values or double bookings.
- Create indexes, which are lookup aids that make searches faster, only where your queries need them.
- Learn PostgreSQL surprises, like foreign key columns not getting an index on their own.
- Decide when to split a very large table into parts, called partitioning.
Who is this for?
It suits developers who build apps on PostgreSQL and want tables that are correct from the start. It also helps when you want a second opinion on a schema before it goes live.
Summarized by AI from this skill's SKILL.md. Suggest a better one
SKILL.md
View on GitHub βUse this skill when designing or reviewing a PostgreSQL-specific schema. Covers best-practices, data types, indexing, constraints, performance patterns, and advanced features
PostgreSQL Table Design
When to Use
- Designing a new PostgreSQL schema, or reviewing one before it ships.
- Choosing column types, keys, constraints, or indexes for PostgreSQL specifically.
- Deciding whether and how to partition a large table, or how to store semi-structured data.
- Planning a schema change on a live database without downtime.
The rules and decision points for a PostgreSQL schema. The full data-type catalog, workload
patterns (update-heavy, insert-heavy, upsert, schema evolution), extensions, JSONB indexing,
and worked DDL examples are in references/details.md; open it when a section below points there.
Core Rules
- Define a PRIMARY KEY for reference tables (users, orders, etc.). Not always needed for time-series/event/log data. When used, prefer
BIGINT GENERATED ALWAYS AS IDENTITY; useUUIDonly when global uniqueness/opacity is needed. - Normalize first (to 3NF) to eliminate data redundancy and update anomalies; denormalize only for measured, high-ROI reads where join performance is proven problematic.
- Add NOT NULL everywhere it is semantically required; use DEFAULTs for common values.
- Create indexes for access paths you actually query: PK/unique (auto), FK columns (manual!), frequent filters/sorts, and join keys.
- Prefer TIMESTAMPTZ for event time; NUMERIC for money; TEXT for strings; BIGINT for integers; DOUBLE PRECISION for floats (or
NUMERICfor exact decimal arithmetic).
PostgreSQL Gotchas
- Identifiers: unquoted β lowercased. Avoid quoted/mixed-case names; use
snake_case. - Unique + NULLs: UNIQUE allows multiple NULLs. Use
UNIQUE NULLS NOT DISTINCT (...)(PG15+) to restrict to one NULL. - FK indexes: PostgreSQL does not auto-index FK columns. Add them.
- No silent coercions: length/precision overflows error out (no truncation). Inserting 999 into
NUMERIC(2,0)fails, unlike databases that silently truncate or round. - Sequences/identity have gaps (normal; don't "fix"). Rollbacks, crashes, and concurrent transactions leave gaps (1, 2, 5, 6...).
- Heap storage: no clustered PK by default;
CLUSTERis a one-off reorganization, not maintained on later inserts. - MVCC: updates/deletes leave dead tuples; vacuum handles themβdesign to avoid hot wide-row churn.
Data Types
- IDs:
BIGINT GENERATED ALWAYS AS IDENTITY;UUIDfor distributed or opaque IDs, generated withuuidv7()(PG18+) orgen_random_uuid(). - Numbers:
BIGINTunless storage is critical;DOUBLE PRECISIONoverREAL;NUMERIC(p,s)for money and exact decimals. - Strings:
TEXT, withCHECK (LENGTH(col) <= n)when a limit is needed;BYTEAfor binary. Case-insensitive lookups: expression index onLOWER(col), orCITEXTwhen a constraint must be case-insensitive. - Time:
TIMESTAMPTZ,DATE,INTERVAL.now()is transaction start;clock_timestamp()is wall clock. - Booleans:
BOOLEAN NOT NULLunless tri-state is required. - Enums:
CREATE TYPE ... AS ENUMonly for small, stable sets; evolving business values getTEXT+CHECKor a lookup table. - JSONB over JSON, indexed with GIN, for optional/semi-structured attributes only.
- Arrays, ranges, network, geometric, full-text, domain, composite, and vector types, plus TOAST storage and collation control: see
references/details.md.
Types to avoid
| Avoid | Use instead |
|---|---|
timestamp (without time zone) | timestamptz |
char(n), varchar(n) | text (+ CHECK on length if needed) |
money | numeric |
timetz | timestamptz |
timestamptz(0) or any precision | timestamptz |
serial | generated always as identity |
Constraints
- PK: implicit UNIQUE + NOT NULL; creates a B-tree index.
- FK: specify
ON DELETE/UPDATE(CASCADE,RESTRICT,SET NULL,SET DEFAULT). Index the referencing column. UseDEFERRABLE INITIALLY DEFERREDfor circular dependencies checked at commit. - UNIQUE: creates a B-tree index; allows multiple NULLs unless
NULLS NOT DISTINCT(PG15+). PreferNULLS NOT DISTINCTunless duplicate NULLs are wanted. - CHECK: row-local; NULL passes (three-valued logic). Combine with
NOT NULL:price NUMERIC NOT NULL CHECK (price > 0). - EXCLUDE: prevents overlaps with operators, e.g.
EXCLUDE USING gist (room_id WITH =, booking_period WITH &&)stops double-booking. Needs a GiST-capable type.
Indexing
- B-tree: default for equality/range (
=,<,>,BETWEEN,ORDER BY). - Composite: leftmost-prefix rule (
WHERE a = ? AND b > ?uses(a,b);WHERE b = ?does not). Most selective columns first. - Covering:
CREATE INDEX ON tbl (id) INCLUDE (name, email)for index-only scans. - Partial: hot subsets,
CREATE INDEX ON tbl (user_id) WHERE status = 'active'. - Expression:
CREATE INDEX ON tbl (LOWER(email)); the query must use the same expression. - GIN: JSONB containment/existence, arrays, full-text search. GiST: ranges, geometry, exclusion constraints.
- BRIN: large, naturally ordered data (time-series) at minimal storage cost; effective when disk order correlates with the indexed column.
Partitioning
- Use for large tables (>100M rows) whose queries consistently filter on the partition key, or where maintenance (pruning, bulk replacement) follows a key.
- RANGE for time-series (
PARTITION BY RANGE (created_at); TimescaleDB automates it with retention and compression), LIST for discrete values, HASH for even distribution without a natural key. - Constraint exclusion: the planner prunes partitions through their
CHECKconstraints; declarative partitioning (PG10+) creates them for you. - Prefer declarative partitioning or hypertables. Do NOT use table inheritance.
- Limitations: no global UNIQUE constraintsβinclude the partition key in PK/UNIQUE. FKs from partitioned tables need PG11+, FKs referencing a partitioned table need PG12+; on older versions, use triggers.
Examples
CREATE TABLE users (
user_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX ON users (LOWER(email));
CREATE INDEX ON users (created_at);
CREATE TABLE orders (
order_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(user_id),
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING','PAID','CANCELED')),
total NUMERIC(10,2) NOT NULL CHECK (total > 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON orders (user_id);
CREATE INDEX ON orders (created_at);
-- JSONB attributes with a generated, indexable scalar
CREATE TABLE profiles (
user_id BIGINT PRIMARY KEY REFERENCES users(user_id),
attrs JSONB NOT NULL DEFAULT '{}',
theme TEXT GENERATED ALWAYS AS (attrs->>'theme') STORED
);
CREATE INDEX profiles_attrs_gin ON profiles USING GIN (attrs);
Going deeper
references/details.md holds the material this file only names:
- The full data-type catalog: TOAST storage, collations, arrays, ranges, network, geometric, text search, domains, composites, vectors.
- Table types (
TEMPORARY,UNLOGGED) and row-level security. - Constraint and index notes, and partitioning DDL for RANGE, LIST, and HASH.
- Workload patterns: update-heavy, insert-heavy, upsert design, safe schema evolution.
- Generated columns and extensions (
pg_trgm,citext,timescaledb,postgis,pgvector, and more). - JSONB indexing strategies, including
jsonb_path_opsand extracted B-tree columns.
More from this repository10
Helps you build a consistent look for your app with Tailwind CSS v4. It sets up shared colors, sizes, dark mode and reusable components so every screen matches.
A guide to TypeScript's advanced type features, which describe the shape of your data so mistakes are caught before the code runs. It covers reusable types, types that change based on rules, and built-in helper types, with short examples.
Guidance for building reliable server-side apps with Node.js, using Express or Fastify. It covers login, error handling, databases, API design and a list of proven best practices.
Intelligent automation and multi-agent orchestration for Claude Code
Claude Code plugin for AI-powered framework migration workflows, coordinating specialized agents to plan and execute transitions between technology stacks as part of the wshobson/agents marketplace.
A Claude Code plugin for payment system integration covering Stripe, PayPal, and billing platforms with checkout flows, subscription management, and webhook handling in the wshobson/agents ecosystem.
A Claude Code plugin bundling core development tools and agents for everyday tasks like debugging and code generation, part of the wshobson/agents marketplace with 73 focused plugins.
Claude Code plugin for observability and monitoring including logging, metrics, distributed tracing, and alerting, part of the wshobson/agents marketplace with 73 focused plugins.
A Claude Code plugin providing specialized AI agents for deployment strategy planning and execution, covering blue-green deployments, canary releases, rolling updates, and zero-downtime patterns.
Claude Code plugin for JVM language development including Java, Kotlin, and Scala with specialized agents for idiomatic patterns, build tools, and runtime optimization in the wshobson/agents marketplace.