iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Data Types

PostgreSQL data types: integers, decimals, text, timestamps, JSON, arrays, UUIDs. Picking the right one early saves migration pain later.

PostgreSQL — datatypes

EXAMPLE
-- ===== Numeric =====
SMALLINT       -- 2 bytes, -32k..32k
INTEGER / INT  -- 4 bytes, ~-2.1B..2.1B
BIGINT         -- 8 bytes
NUMERIC(p, s)  -- exact decimal, use for money: NUMERIC(12, 2)
REAL           -- 4-byte float
DOUBLE PRECISION -- 8-byte float

CREATE TABLE orders (
  id         BIGSERIAL PRIMARY KEY,    -- 8-byte auto-increment
  total      NUMERIC(12, 2) NOT NULL,
  units      INTEGER NOT NULL
);

-- ===== Text =====
TEXT       -- unlimited; this is the right default
VARCHAR(n) -- arbitrary limit; rarely earns the maintenance cost
CHAR(n)    -- fixed-length, pads with spaces; almost never the right tool

-- All three have the same performance in Postgres; pick TEXT and add CHECKs for length.

ALTER TABLE orders ADD COLUMN note TEXT CHECK (char_length(note) <= 500);

-- ===== Booleans =====
BOOLEAN  -- TRUE / FALSE / NULL

-- ===== Date / time =====
DATE                    -- 4 bytes
TIMESTAMP               -- 8 bytes, NO timezone (rarely the right choice)
TIMESTAMPTZ             -- 8 bytes, with tz (always store this)
INTERVAL                -- '1 day', '2 hours 30 minutes'
TIME                    -- time of day, no date

CREATE TABLE events (
  id          BIGSERIAL PRIMARY KEY,
  occurred_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  duration    INTERVAL
);

-- Pattern: store in TIMESTAMPTZ, format/convert at the edge.

-- ===== UUID =====
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE users (
  id    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE NOT NULL
);

-- ===== JSON =====
JSON   -- raw text; preserves order + whitespace
JSONB  -- decomposed binary; indexed; use this by default

CREATE TABLE profiles (
  user_id UUID PRIMARY KEY,
  data    JSONB NOT NULL
);

-- Index a specific path:
CREATE INDEX profiles_data_country ON profiles ((data->>'country'));

-- Query:
SELECT user_id FROM profiles WHERE data @> '{"country": "AU"}';
SELECT data->>'email' FROM profiles WHERE data ? 'verified';

-- ===== Arrays =====
CREATE TABLE posts (
  id   BIGSERIAL PRIMARY KEY,
  tags TEXT[] NOT NULL DEFAULT '{}'
);

SELECT id FROM posts WHERE 'sql' = ANY(tags);
CREATE INDEX posts_tags_gin ON posts USING GIN (tags);

-- ===== Enum =====
CREATE TYPE order_status AS ENUM ('new', 'paid', 'shipped', 'cancelled');
ALTER TABLE orders ADD COLUMN status order_status NOT NULL DEFAULT 'new';

-- Enum values are ordered as defined; add new values with ALTER TYPE ... ADD VALUE.

-- ===== Network types =====
INET    -- IPv4 or IPv6 address
CIDR    -- network address
MACADDR -- 6-byte hardware address

CREATE TABLE logins (
  user_id UUID NOT NULL,
  ip      INET NOT NULL,
  at      TIMESTAMPTZ NOT NULL
);

-- ===== Generated / computed =====
ALTER TABLE orders
  ADD COLUMN total_cents BIGINT GENERATED ALWAYS AS ((total * 100)::bigint) STORED;

-- ===== Patterns to internalise =====
-- - TEXT for strings; CHECK for length when you need it
-- - TIMESTAMPTZ everywhere; never TIMESTAMP without tz
-- - NUMERIC for money; never DOUBLE PRECISION
-- - JSONB > JSON; index frequently-queried paths
-- - UUID PKs with gen_random_uuid() for distributed-friendly ids
-- - Enums for closed sets; reach for a lookup table when membership changes often

-- ===== Pitfalls =====
-- - DOUBLE PRECISION for money -> rounding errors in totals
-- - TIMESTAMP WITHOUT TIME ZONE -> daylight-saving + tz-conversion bugs forever
-- - VARCHAR(n) with arbitrary n -> migrations every time someone enters longer text
-- - JSONB without an index -> sequential scans on every search
-- - Enums with values you outgrow -> alter-add is fine, alter-rename is risky

Why it matters

Datatypes are the schema decision you cannot easily undo. TEXT + TIMESTAMPTZ + NUMERIC + JSONB + UUID covers most of what an app needs. Reach for arrays and enums when they map cleanly, and avoid VARCHAR(n) magic numbers.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
-- numeric: int, bigint, numeric(10,2), real, double precision
-- text:    text, varchar(n), char(n)
-- time:    date, time, timestamp, timestamptz, interval
-- bool:    boolean
-- json:    json, jsonb
-- other:   uuid, bytea, inet, ARRAY
Try it Yourself »

Discussion

Loading…