Relational Databases (MySQL, Postgres)
The default storage layer for structured data — and still the right choice for a huge fraction of what an ML system needs to store, even in an AI-first stack.
The Relational Model
Data lives in tables (rows and columns), with relationships between tables expressed via foreign keys rather than nesting data inside itself. Normalization organizes tables to minimize redundancy — e.g. storing a user's address once in a users table rather than repeating it on every one of their orders — trading some query complexity (more joins) for data consistency (update it in one place).
SQL Fundamentals
- Joins: combine rows from multiple tables based on a related column — the mechanism that makes normalization practical to query.
- Aggregations:
GROUP BYwithCOUNT,SUM,AVG, etc. — computing summary statistics, exactly the kind of query you'd run to build training features from raw event logs. - Window functions: compute values across a set of rows related to the current row (running totals, rankings, moving averages) without collapsing them into a single aggregated row — extremely useful for feature engineering over time-series-like data (e.g. "this user's average order value over their last 5 orders").
| id | name |
|---|---|
| 1 | Ada |
| 2 | Grace |
| 3 | Alan |
| order_id | customer_id | item |
|---|---|---|
| 101 | 2 | Widget |
| 102 | 3 | Gadget |
| 103 | 4 | Gizmo |
| c.id | c.name | o.order_id | o.item |
|---|---|---|---|
| 2 | Grace | 101 | Widget |
| 3 | Alan | 102 | Gadget |
Indexing and Query Planning
An index is an auxiliary data structure (typically a B-tree — see Algorithms & Data Structures) that lets the database find rows matching a condition without scanning the entire table, turning an scan into roughly lookup. The tradeoff: indexes speed up reads but slow down writes (every insert/update must also update the index) and consume extra storage — so indexing is a deliberate design decision based on actual query patterns, not something to apply everywhere by default.
The query planner decides how to execute a given SQL query (which indexes to use, what order to join tables in) — understanding how to read a query plan (EXPLAIN in both MySQL and Postgres) is the core skill for diagnosing a slow query.
Transactions and ACID
- Atomicity: a transaction either fully completes or fully rolls back — no partial writes.
- Consistency: a transaction takes the database from one valid state to another, respecting all constraints.
- Isolation: concurrent transactions don't see each other's uncommitted changes.
- Durability: once committed, a transaction survives a crash.
These guarantees matter whenever correctness under concurrent access is non-negotiable — financial transactions, inventory counts, anything where a race condition would cause real damage.
When Relational Is (and Isn't) the Right Tool
Relational databases excel at structured data with well-defined relationships and a need for strong consistency — user accounts, transactional records, ML experiment/model metadata. They're a poor fit for storing and searching high-dimensional embeddings efficiently at scale (see Vector Databases) or for deeply connected, multi-hop relationship queries (see Graph Databases) — which is exactly why modern AI systems typically use relational databases alongside vector and graph stores, not instead of them.
Next: PostgreSQL — a deep dive into the system you'll actually run: schemas and types, writing queries end to end, indexing, transactions, and Postgres-specific features like JSONB and pgvector.