Designing Database Schema

1. Normalizing Database Tables

FormRule
1NFAtomic columns, no repeating groups
2NF1NF + no partial key dep
3NF2NF + no transitive dep on PK
BCNFStronger 3NF; every determinant is a key

2. Denormalizing for Performance

TechniqueUse
Redundant columnAvoid join for hot read
Aggregate columnPre-computed totals
Materialized viewRefreshable derived data
Read model (CQRS)Separate denormalized read store
Warning: Denormalize only with measurement; pay maintenance cost.

3. Designing Primary Keys

TypeProsCons
Auto-increment BIGINTCompact, fast indexSequential leak; not distributed-friendly
UUID v4No coordinationRandom; index fragmentation
UUID v7 / ULIDTime-sortable, distributed16 bytes
CompositeNatural keysAwkward FK references

4. Implementing Foreign Key Relationships

ActionBehavior
ON DELETE CASCADEDelete children
ON DELETE SET NULLNull FK
ON DELETE RESTRICTBlock delete
ON UPDATE CASCADEPropagate PK change (rare)

5. Designing Indexes

IndexWhen
B-treeEquality, range, ORDER BY
HashEquality only (specific engines)
CompositeMulti-column WHERE (left-most rule)
CoveringIncludes all SELECT columns
PartialSubset of rows (WHERE clause in index)
GIN/GISTFull-text, JSON, geo (PostgreSQL)

6. Implementing Soft Deletes

Example: Soft-delete column

ALTER TABLE orders ADD COLUMN deleted_at TIMESTAMPTZ NULL;
CREATE INDEX idx_orders_active ON orders(customer_id) WHERE deleted_at IS NULL;

7. Implementing Audit Columns

ColumnType
created_atTIMESTAMPTZ DEFAULT now()
created_byUUID / VARCHAR
updated_atTIMESTAMPTZ; trigger or app
updated_byUUID / VARCHAR
versionBIGINT for optimistic lock

8. Designing Partitioning Strategies

StrategyUse Case
RangeTime-series (monthly partitions)
ListTenant ID, region
HashEven distribution by key
CompositeRange + hash combinations

9. Using Junction Tables

Example: M:N junction

CREATE TABLE user_roles (
  user_id UUID NOT NULL REFERENCES users(id),
  role_id UUID NOT NULL REFERENCES roles(id),
  granted_at TIMESTAMPTZ DEFAULT now(),
  PRIMARY KEY (user_id, role_id)
);
CREATE INDEX idx_user_roles_role ON user_roles(role_id);

10. Implementing Polymorphic Associations

PatternTrade-off
Type column + IDSimple; no FK enforcement
Multiple FK columnsFK enforced; sparse
Single-table inheritanceOne wide table; sparse columns
Joined inheritanceMultiple tables + join

11. Designing Column Types

DataType
MoneyNUMERIC(19,4) or BIGINT cents
BooleanBOOLEAN (avoid CHAR(1))
TimestampTIMESTAMPTZ (always with TZ)
EnumVARCHAR + CHECK or DB ENUM
TextTEXT (no length penalty in PG)
BinaryBYTEA / BLOB or external object store
JSONJSONB (PostgreSQL)

12. Implementing Database Constraints

ConstraintPurpose
NOT NULLRequired field
UNIQUENo duplicates
CHECKBusiness invariant (amount >= 0)
FOREIGN KEYReferential integrity
EXCLUDERange non-overlap (PG)