Overview
SQL databases (relational databases) store data in tables of rows and columns with a fixed schema, relate tables through keys, and let you query with SQL, a declarative language where you describe what you want and the optimizer decides how. They provide ACID transactions, so multi-step changes like transferring money either fully happen or not at all.
Relational databases are the default choice for most business data because of strong consistency, flexible queries (joins, aggregations), constraints that protect data integrity, and decades of tooling. Their scaling limit is writes on a single primary, handled with vertical scaling, replicas for reads, and eventually sharding.
Each drawer is a table with labeled folders in a fixed format. Folders reference each other by ID, and a strict clerk (constraints) refuses to file anything incomplete or duplicated.
When to use it
- Structured data with relationships: users, orders, payments, inventory.
- You need transactions and strong consistency.
- Ad hoc queries, reporting, joins, and aggregations.
- Data integrity rules: unique constraints, foreign keys.
Where it shows up in interviews
Recognize it when: money, inventory, bookings, orders.
- Design a payment system
- Design a ticket booking system
- Design an e-commerce order service
Recognize it when: interviewer asks which database and why.
- Design Twitter
- Design Uber trips storage
Where it is used in real software
Power companies from startups to giants; Shopify and GitHub run on sharded MySQL, Instagram started on PostgreSQL.
Google Spanner, CockroachDB, and YugabyteDB offer SQL with horizontal scaling and global transactions.
Amazon RDS / Aurora, Cloud SQL, and Azure SQL handle backups, replicas, and failover.
Key terms
- Schema
- Table definitions: columns, types, constraints.
- Primary key
- Unique identifier of a row.
- Foreign key
- A column referencing another table's primary key.
- ACID
- Atomicity, Consistency, Isolation, Durability.
- Query optimizer
- Chooses indexes, join order, and algorithms for a query.
How it works, step by step
- 1Model entities as tables
users, orders, order_items, products.
- 2Define keys and constraints
Primary keys, foreign keys, NOT NULL, UNIQUE, CHECK.
- 3Normalize, then denormalize deliberately
Avoid duplicate data by default; copy data only for measured read needs.
- 4Add indexes for query patterns
Based on WHERE, JOIN, and ORDER BY columns.
- 5Scale reads, then writes
Caching and read replicas first; partitioning and sharding when writes outgrow one primary.
A simple e-commerce schema
Core tables and relationships
| Table | Key columns | Relationships |
|---|---|---|
| users | id PK, email UNIQUE | - |
| products | id PK, sku UNIQUE, price_cents | - |
| orders | id PK, user_id FK, status, created_at | users 1 - N orders |
| order_items | (order_id, product_id) PK, qty, price_cents | orders 1 - N items, products 1 - N items |
NOWTable: users | Key columns: id PK, email UNIQUE | Relationships: -
order_items stores price_cents at purchase time on purpose: historical orders must not change when product prices change.
Implementation
CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, email TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now()); CREATE TABLE orders ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id), status TEXT NOT NULL CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled')), total_cents BIGINT NOT NULL CHECK (total_cents >= 0), created_at TIMESTAMPTZ NOT NULL DEFAULT now());CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC); -- Revenue per day for the last weekSELECT date_trunc('day', created_at) AS day, sum(total_cents) / 100.0 AS revenueFROM ordersWHERE status = 'paid' AND created_at > now() - interval '7 days'GROUP BY 1ORDER BY 1;Complexity and performance
B-tree index.
Hardware and schema dependent.
Beyond that, partition or shard.
Trade-offs
Relational databases make strong consistency easy but scaling writes beyond one primary requires sharding or distributed SQL.
Schemas catch bad data early but require migrations for changes; JSON columns provide flexibility for semi-structured fields.
Variants and related techniques
Transactional databases (PostgreSQL) vs analytical column stores (Snowflake, BigQuery, ClickHouse).
Spanner and CockroachDB shard automatically with consensus replication.
Common mistakes
- Choosing NoSQL by default for 'scale'.
Fix: Most products fit comfortably in a well-indexed relational database for a long time.
- Floating point for money.
Fix: Store integer cents or DECIMAL.
- Missing indexes on foreign keys.
Fix: Index columns used in joins and filters.
Interview questions
When would you choose SQL over NoSQL?
When data is relational, integrity and transactions matter (payments, inventory), and queries are varied. Choose NoSQL for massive scale with simple access patterns, flexible schemas, or specialized models.
How do you scale a relational database?
Optimize queries and indexes, scale vertically, add caching and read replicas, partition large tables, and finally shard by a key like tenant or user ID, or move to distributed SQL.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design the schema for a library system | Easy | Keys and relations. |
| Design the schema for a ticket booking system | Medium | Constraints to prevent double booking. |