Skip to main content

πŸ—„οΈ Relational vs NoSQL Databases

Week 8: Databases β€” course module banner illustration

Every application you've built so far kept its data in memory β€” the moment the server restarted, everything vanished. A database is how software remembers things permanently. This lesson introduces the two great families of databases, so that when you sit down to build a real app you can choose the right tool instead of the fashionable one.

Week 8 · Day 1 (Monday: Introduction to Databases) · Lecture 1

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain what a database is and why every real application needs one
  • Describe how relational databases model data as tables, rows, and schema
  • Define ACID and contrast it with the BASE model used by many NoSQL systems
  • Identify the four families of NoSQL databases and a use case for each
  • Compare vertical and horizontal scaling and connect them to database choice
  • Apply a decision framework to pick relational, NoSQL, or both for a given app

Estimated Time: 55 minutes

Practice: Model the same blog data two ways β€” relational tables and a NoSQL document β€” then justify a database choice for three scenarios.

In This Lesson

What Is a Database?

A database is an organized collection of data plus the software that lets you store, retrieve, and update it reliably. That software is called a Database Management System (DBMS) β€” PostgreSQL and MongoDB are both DBMSs.

Think of a library. Without the Dewey Decimal System, a million books would be an unsearchable pile. The cataloguing system is what turns that pile into something you can query: "find every book by this author, published after 2020." A database does exactly this for your application's data, at a scale and speed a filing cabinet never could.

graph LR U[Users] --> A[Application] A -->|Write & Query| B[Database System] B -->|Return Results| A B --- C[(Persistent Storage)]

Your application never touches the raw files on disk. It sends requests to the DBMS, which handles the hard parts β€” concurrent access, crash recovery, indexing, permissions β€” and hands back exactly the data you asked for. From the moment we add a database, our app can survive restarts, serve many users at once, and answer questions about millions of records in milliseconds.

Databases split into two broad paradigms, each with a different philosophy about how data should be shaped:

  • Relational (SQL) β€” data lives in strict tables with predefined columns; relationships are first-class.
  • NoSQL ("Not Only SQL") β€” a family of systems that relax the table structure to gain flexibility and horizontal scale.

Relational Databases (SQL)

Relational databases, invented at IBM in the 1970s, organize data into tables (also called relations). A table is a grid: each row is one record, each column is one attribute. Before you store anything, you declare a schema β€” the exact columns and their types β€” and every row must obey it.

A users table linked by a foreign key to a roles table users id: 1 name: "Ada" email: "ada@ex.com" role_id: 2 β–² foreign key roles id: 2 name: "Admin" can_edit: true can_delete: true β–² primary key
The role_id column in users points at the id of a row in roles. Store each fact once; link to it with keys.

The vocabulary of relational data

  • Primary key β€” a column (often id) that uniquely identifies each row.
  • Foreign key β€” a column that references a primary key in another table, creating a relationship.
  • Schema β€” the fixed definition of tables, columns, and types, declared up front.
  • Join β€” an operation that combines rows from related tables into one result.
  • Normalization β€” organizing data so each fact is stored exactly once (covered in depth two lessons from now).

Popular relational systems

RDBMSBest known for
PostgreSQLOpen-source, standards-compliant, rich features β€” our choice this week
MySQL / MariaDBUbiquitous open-source database behind much of the web
SQLiteA whole database in a single file β€” perfect for tests and small apps
SQL Server / OracleEnterprise-grade commercial systems

What relational code looks like

You define structure with SQL, then query across relationships with a JOIN:

-- Two tables linked by a foreign key
CREATE TABLE users (
    id         SERIAL PRIMARY KEY,          -- auto-incrementing unique id
    username   VARCHAR(50)  UNIQUE NOT NULL,
    email      VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP    DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE posts (
    id           SERIAL PRIMARY KEY,
    user_id      INTEGER REFERENCES users(id),  -- foreign key -> users.id
    title        VARCHAR(200) NOT NULL,
    published_at TIMESTAMP
);

-- Combine the two tables to answer one question
SELECT users.username, posts.title
FROM users
JOIN posts ON users.id = posts.user_id
WHERE posts.published_at > '2024-01-01';

Result

username | title
---------+------------------------
 ada     | Learning SQL
 grace   | Foreign keys explained

βœ… Why relationships matter

Because the user's email lives in one place (the users table), changing it updates it everywhere at once. There is no risk of some posts showing the old address. This "store it once" guarantee is the superpower of relational design.

ACID: The Reliability Promise

Relational databases are trusted with money, medical records, and airline seats because they guarantee ACID transactions. A transaction is a group of operations treated as one indivisible unit.

LetterMeaningIn plain English
AtomicityAll or nothingEither every step succeeds, or the whole transaction is rolled back β€” no half-finished states
ConsistencyRules always holdThe database moves from one valid state to another; constraints are never violated
IsolationNo cross-talkConcurrent transactions don't see each other's half-done work
DurabilitySurvives crashesOnce committed, the data stays saved even if the power dies a millisecond later

The classic example is a bank transfer. Moving $100 from account A to account B is really two steps: debit A, credit B. Atomicity guarantees you can never lose the money in between β€” if the credit fails, the debit is undone too.

BEGIN;                                       -- start the transaction
  UPDATE accounts SET balance = balance - 100 WHERE id = 'A';
  UPDATE accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT;                                       -- both succeed together, or ROLLBACK undoes both

NoSQL Databases

NoSQL means "Not Only SQL." These systems arose in the 2000s when web giants hit the limits of fitting one enormous relational database on one very large server. NoSQL databases trade some of the rigid structure and strong guarantees of relational systems for flexible schemas and the ability to scale horizontally β€” spreading data across many cheap servers.

ACID's relaxed cousin: BASE

Where relational systems chase ACID, many distributed NoSQL systems follow BASE: Basically Available, Soft state, Eventual consistency. The idea: stay fast and available even under network trouble, and let all copies of the data converge to the same value shortly after a write rather than instantly.

πŸ’‘ Eventual consistency, concretely

You update your profile photo. For a second or two, a friend on the other side of the world might still see the old one while the change propagates across data centers. For a social feed that's perfectly fine. For your bank balance, it is not β€” which is exactly why the two paradigms exist.

The four families of NoSQL

graph TD A[NoSQL] --> B[Document Stores] A --> C[Key-Value Stores] A --> D[Wide-Column Stores] A --> E[Graph Databases] B --> B1[MongoDB] B --> B2[CouchDB] C --> C1[Redis] C --> C2[DynamoDB] D --> D1[Cassandra] D --> D2[HBase] E --> E1[Neo4j] E --> E2[Neptune]

1. Document stores

Store data as flexible, JSON-like documents. Each document is self-contained and two documents in the same collection need not have identical fields. This maps beautifully to JavaScript objects.

// A MongoDB document β€” note the nested profile and array of posts
{
  "_id": ObjectId("5f8d5f"),
  "username": "ada",
  "email": "ada@example.com",
  "profile": {
    "age": 28,
    "interests": ["coding", "hiking"]
  },
  "posts": [
    { "title": "My first post", "likes": 12 },
    { "title": "Learning NoSQL", "likes": 34 }
  ]
}

Great for: content management, product catalogs, user profiles β€” anything whose shape varies or evolves. Examples: MongoDB, CouchDB, Firestore.

2. Key-value stores

The simplest model: a giant dictionary of key β†’ value. Blazingly fast for lookups by key, but you can't query the values themselves.

// Redis: store a session, read it back, expire it in 1 hour
SET session:abc123 '{"userId":42,"role":"admin"}'
GET session:abc123
EXPIRE session:abc123 3600

Great for: caching, session storage, real-time leaderboards. Examples: Redis, Amazon DynamoDB, Memcached.

3. Wide-column stores

Store data in tables, but each row can have different columns, and columns are grouped into families optimized for huge write volumes across many machines.

Great for: time-series data, IoT sensor streams, event logging at massive scale. Examples: Apache Cassandra, HBase, ScyllaDB.

4. Graph databases

Model data as nodes connected by edges, making relationship-heavy queries ("friends of friends who like hiking") fast and natural.

// Neo4j Cypher: who are Ada's friends?
MATCH (u:Person {name: 'Ada'})-[:FRIENDS_WITH]->(friend)
RETURN friend.name

Great for: social networks, recommendation engines, fraud detection. Examples: Neo4j, Amazon Neptune, ArangoDB.

Same Data, Two Shapes

The clearest way to feel the difference is to model the same information both ways. Here is one blog user who wrote two posts.

Relational splits data across linked tables; a document nests it in one place Relational: split & linked users id:1 name:"Ada" email:"ada@ex.com" posts id:10 user_id:1 title:"My first post" id:11 user_id:1 title:"Learning NoSQL" Document: nested in one { name: "Ada", email: "ada@ex.com", posts: [ { title: "My first post" }, { title: "Learning NoSQL" } ] }
Relational stores each entity in its own table and joins them on demand. A document store nests the posts right inside the user β€” one read gets everything.

Neither is "better." The relational version makes it trivial to ask "how many posts total across all users?" and guarantees a post can't reference a user who doesn't exist. The document version fetches a user and all their posts in a single, fast read β€” no join required β€” but duplicates structure and makes cross-user queries harder.

Choosing Between Them

FactorRelational (SQL)NoSQL
Data structureTables with fixed columnsDocuments, key-value, wide-column, or graph
SchemaFixed, defined up frontFlexible, can vary per record
ScalingVertical (a bigger server)Horizontal (more servers)
ConsistencyACID β€” strong, immediateOften BASE β€” eventual
RelationshipsJoins on foreign keysEmbedding or manual references
Query languageSQL (standardized)Varies by database

Vertical vs horizontal scaling

When one server can't keep up, you have two options. Vertical scaling ("scale up") means buying a beefier machine β€” more CPU, more RAM. It's simple but has a ceiling and gets expensive fast. Horizontal scaling ("scale out") means adding more ordinary machines and spreading the data across them. NoSQL systems were built for this; classic relational databases find it harder because joins and ACID guarantees are tricky across many machines.

βœ… Reach for relational when…

  • Data has clear, stable relationships (users, orders, products)
  • You need complex queries and multi-step transactions
  • Correctness is non-negotiable β€” money, inventory, bookings
  • You want one standardized query language across the team

πŸ’œ Reach for NoSQL when…

  • Your data is unstructured or its shape changes often
  • You need to ingest huge write volumes or scale to many servers
  • Access patterns are simple and known ahead of time (fetch by key, fetch a document)
  • You're caching, storing sessions, or modeling a network of connections
πŸ’‘ Default advice: For most web apps you're learning to build, start with a relational database like PostgreSQL. It's flexible enough for the vast majority of projects, and its guarantees save you from a category of bugs you don't want to debug. Add NoSQL when a specific need appears.

Polyglot Persistence

Real production systems rarely pick just one. Polyglot persistence means using the right database for each job within a single application. Netflix, Airbnb, and Uber all mix several.

graph TD A[E-commerce App] --> B[Orders & Payments] A --> C[Product Catalog] A --> D[Sessions & Cart] A --> E[Recommendations] B --> B1[PostgreSQL - relational] C --> C1[MongoDB - document] D --> D1[Redis - key-value] E --> E1[Neo4j - graph]

Orders go in PostgreSQL because money demands ACID. The flexible product catalog fits a document store. Sessions live in Redis for speed. "Customers who bought this also bought…" is a graph problem. Each database earns its place by doing one thing exceptionally well.

Practice & Quiz

πŸ‹οΈ Exercise 1: Choose the database

Goal: For each scenario, decide relational or NoSQL (and which NoSQL family if so), and justify it in one sentence.

  1. A bank that processes account transfers
  2. A cache that stores the result of an expensive computation, looked up by a string key
  3. A blog CMS whose article structure keeps changing as editors add fields
  4. A "people you may know" feature on a social network
  5. An IoT platform ingesting millions of temperature readings per minute
πŸ’‘ Hint

Ask two questions for each: does correctness require ACID transactions, and is the access pattern "by key," "by document," "by relationship," or "complex queries and joins"?

βœ… Solution
  1. Relational. Transfers need ACID atomicity so money is never lost mid-transfer.
  2. NoSQL β€” key-value (Redis). Pure lookup by key, must be fast, no need to query the values.
  3. NoSQL β€” document (MongoDB). Flexible schema absorbs new fields without migrations.
  4. NoSQL β€” graph (Neo4j). "Friends of friends" is a relationship traversal, graphs' specialty.
  5. NoSQL β€” wide-column (Cassandra). Built for enormous, fast, append-heavy writes across many servers.

πŸ‹οΈ Exercise 2: Model it both ways

Goal: A recipe has a title, a list of ingredients, and an author. Sketch (a) a relational design with tables and keys, and (b) an equivalent MongoDB document.

βœ… Solution

Relational β€” three tables so ingredients and authors aren't duplicated:

CREATE TABLE authors (
    id   SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);
CREATE TABLE recipes (
    id        SERIAL PRIMARY KEY,
    title     VARCHAR(200) NOT NULL,
    author_id INTEGER REFERENCES authors(id)
);
CREATE TABLE ingredients (
    id        SERIAL PRIMARY KEY,
    recipe_id INTEGER REFERENCES recipes(id),
    name      VARCHAR(100),
    amount    VARCHAR(50)
);

Document β€” everything nested in one recipe:

{
  "title": "Adobo",
  "author": { "name": "Ray" },
  "ingredients": [
    { "name": "chicken", "amount": "1 kg" },
    { "name": "soy sauce", "amount": "1/2 cup" },
    { "name": "vinegar", "amount": "1/2 cup" }
  ]
}

Trade-off: the document reads in one shot, but if two recipes share an author and the author's name changes, you must update every document. Relational updates the one authors row.

🎯 Quick Quiz

Question 1: What does the "A" in ACID guarantee?

Question 2: Which NoSQL family best fits caching session data looked up by a single key?

Question 3: "Scaling out by adding more ordinary servers" describes which approach?

Best Practices & Pitfalls

βœ… Do

  • Start with a relational database unless you have a concrete reason not to
  • Let the app's access patterns and consistency needs drive the choice, not hype
  • Use ACID transactions whenever data must stay correct together (transfers, inventory)
  • Mix databases deliberately (polyglot persistence) when one job genuinely needs a different tool

❌ Don't

  • Pick NoSQL just because it sounds modern β€” "schemaless" still needs a thought-out data model
  • Assume NoSQL means "no rules" β€” you still design for how you'll read the data
  • Expect eventual-consistency systems to behave like a bank ledger
  • Bolt on a second database before a single well-designed one has actually hit a wall

⚠️ "NoSQL scales better" is only half true

NoSQL scales writes across servers more easily, but you pay in weaker guarantees and harder cross-record queries. Modern PostgreSQL scales to enormous workloads too. Choose for your data's shape and rules, not a benchmark headline.

Summary

πŸŽ‰ Key Takeaways

  • A database lets your app store data permanently and query it reliably at scale
  • Relational databases use fixed tables, foreign keys, joins, and ACID transactions
  • NoSQL trades rigid schema for flexibility and horizontal scale, often under the BASE model
  • The four NoSQL families are document, key-value, wide-column, and graph
  • Choose by your data's shape and consistency needs β€” and real apps often use several databases together

πŸ“š Additional Resources

πŸš€ What's Next?

You now know which kind of database to reach for. Next we learn to actually talk to a relational one: SQL Basics β€” creating tables, and writing SELECT, INSERT, UPDATE, and DELETE to command your data.

πŸŽ‰ Great start to Week 8!

Data persistence is the backbone of every real application. You've just learned how to think about where it lives.