ALTER TABLE customers ADD CONSTRAINT customers_pkey PRIMARY KEY (id);-- Concurrently (large tables)CREATE UNIQUE INDEX CONCURRENTLY customers_pkey ON customers(id);ALTER TABLE customers ADD CONSTRAINT customers_pkey PRIMARY KEY USING INDEX customers_pkey;
Property
Detail
Implies
UNIQUE + NOT NULL
Per table
Only one PK
Backed by
Unique btree index
2. Adding UNIQUE Constraint
ALTER TABLE customers ADD CONSTRAINT customers_email_key UNIQUE (email);ALTER TABLE orders ADD CONSTRAINT orders_unique UNIQUE NULLS NOT DISTINCT (customer_id, code);
Clause
Effect
NULLS DISTINCT
Default; multiple NULLs allowed
NULLS NOT DISTINCT
Treat NULLs as equal PG 15+
3. Adding NOT NULL Constraint
ALTER TABLE customers ALTER COLUMN email SET NOT NULL;-- Safe pattern on large tablesALTER TABLE customers ADD CONSTRAINT email_nn CHECK (email IS NOT NULL) NOT VALID;ALTER TABLE customers VALIDATE CONSTRAINT email_nn;
4. Adding CHECK Constraint
ALTER TABLE orders ADD CONSTRAINT orders_total_pos CHECK (total_cents >= 0) NOT VALID;ALTER TABLE orders VALIDATE CONSTRAINT orders_total_pos;
Flag
Effect
NOT VALID
Only enforced for new rows; no scan
VALIDATE CONSTRAINT
Background scan, ShareUpdateExclusiveLock
5. Adding DEFAULT Values
ALTER TABLE orders ALTER COLUMN status SET DEFAULT 'new';ALTER TABLE events ALTER COLUMN created_at SET DEFAULT now();
6. Adding FOREIGN KEY Constraint
ALTER TABLE order_items ADD CONSTRAINT order_items_order_fk FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE ON UPDATE NO ACTION;
Note: Always index the referencing column to avoid full scans on parent updates/deletes.
7. Using ON DELETE Actions
Action
Behavior on Parent Delete
NO ACTION
Default; deferred error
RESTRICT
Immediate error
CASCADE
Delete child rows
SET NULL
Set FK column NULL
SET DEFAULT
Set FK column to default
SET NULL (col)
Subset of FK columns PG 15+
8. Using ON UPDATE Actions
Action
Behavior on Parent PK Change
NO ACTION
Deferred error if children
CASCADE
Propagate new key value
SET NULL / SET DEFAULT
As above
9. Creating Named Constraints
ALTER TABLE products ADD CONSTRAINT products_price_nonneg CHECK (price >= 0);
Reason
Benefit
Predictable
Easier to DROP/VALIDATE
Diff-friendly
Stable in migrations
10. Adding Column Constraints
CREATE TABLE products ( id bigserial PRIMARY KEY, sku text UNIQUE NOT NULL, price numeric(12,2) CHECK (price >= 0));
11. Adding Table Constraints
CREATE TABLE bookings ( user_id int, slot tstzrange, PRIMARY KEY (user_id, slot), CONSTRAINT no_overlap EXCLUDE USING gist (user_id WITH =, slot WITH &&));
12. Deferring Constraints
ALTER TABLE order_items ALTER CONSTRAINT order_items_order_fk DEFERRABLE INITIALLY DEFERRED;BEGIN; SET CONSTRAINTS ALL DEFERRED; -- bulk swap parent/child rowsCOMMIT;
Mode
Check When
NOT DEFERRABLE
Per-statement (default)
DEFERRABLE INITIALLY IMMEDIATE
Per-statement; can defer per-tx
DEFERRABLE INITIALLY DEFERRED
At commit
13. Using Exclusion Constraints
CREATE EXTENSION IF NOT EXISTS btree_gist;CREATE TABLE meetings ( room_id int NOT NULL, slot tstzrange NOT NULL, EXCLUDE USING gist (room_id WITH =, slot WITH &&));
14. Dropping Constraints
ALTER TABLE orders DROP CONSTRAINT orders_total_pos;ALTER TABLE orders DROP CONSTRAINT IF EXISTS orders_total_pos;
15. Disabling Constraints
Constraint
How To Bypass
FK
Drop+recreate or session_replication_role='replica'
CHECK
No disable; drop or use NOT VALID + skip validation
Triggers / FKs
ALTER TABLE ... DISABLE TRIGGER ALL
Warning: PostgreSQL has no DISABLE CONSTRAINT; the closest is session_replication_role (superuser-only) or temporarily dropping the constraint.