PostgreSQL's richer features
🏠The everyday picture
Moving from a basic toolbox to a full workshop: the same jobs, but now there's a proper tool for each one.
📘What it really is
Convert your SQLite schema to proper PostgreSQL types and features:
- Ids:
id uuid PRIMARY KEY DEFAULT gen_random_uuid()orid bigint GENERATED ALWAYS AS IDENTITY. -
Times:
created_at timestamptz NOT NULL DEFAULT now(). Date functions:date_trunc('month', placed_at),now() - interval '30 days'. -
Money:
numeric(12, 2)orbigintpaise. -
Fixed lists: a
CHECK (status IN (...))constraint, or an enum type:CREATE TYPE enquiry_status AS ENUM ('new', 'in-progress', 'closed');. -
Case-insensitive uniqueness:
CREATE UNIQUE INDEX ON customers (lower(email));. -
Flexible data:
jsonbcolumns, queried with operators (details->>'source') and indexable with GIN indexes. Use sparingly (Module 51, Lesson 4). -
Full-text search: search by words, with ranking, in many languages:
SELECT name FROM customers
WHERE to_tsvector('simple', name || ' ' || coalesce(notes, '')) @@ plainto_tsquery('simple', 'kumari patna');
(A stored generated column with a GIN index makes it fast.)
-
Upsert: insert, or update if it already exists:
INSERT ... ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;. -
Row locking:
SELECT ... FOR UPDATEinside a transaction (Module 57, Lesson 2). - Partial indexes: index only what you query:
CREATE INDEX ON enquiries (created_at) WHERE status = 'new';.
🖱️Try it
Rewrite your CRM migrations for PostgreSQL types (uuid or identity ids, timestamptz, constraints, case-insensitive
email uniqueness). Add a full-text search over customer names and notes. Try an upsert that adds or updates a customer by
email.
✅Quick check
Which PostgreSQL type should store "when an order was placed"?
Show the answer
timestamptz (timestamp with time zone).
What does ON CONFLICT ... DO UPDATE do?
Show the answer
Inserts the row, or updates the existing one if it conflicts with a unique constraint (an "upsert").
