SQL vs NoSQL: When to Use Each

Reviewed & published by Brayan K

"NoSQL" is not one thing, and it is not a replacement for SQL. By the end of this lesson you'll understand the four main NoSQL families — document, key-value, wide-column, and graph — how ACID and BASE differ, what the CAP theorem really forces you to choose, and how to pick the right store for a job. The honest headline: a relational database is still the right default for most applications.

Part of the free SQL course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn

Our Running Example: users & orders

We model the same small dataset — a few users who placed a few orders — in every storage style, so you can see how each one shapes the same information differently.

1. The Relational Baseline

A relational database (PostgreSQL, MySQL, SQL Server, SQLite, Oracle) stores data in tables of typed columns and rows, with explicit relationships expressed by foreign keys. The structure is fixed up front — that's schema-on-write — and the engine guarantees ACID transactions. This is the model the rest of this course teaches, and it is the right starting point for the overwhelming majority of applications.

A relational database is a well-organized filing cabinet: every drawer (table) holds the same kind of form (rows with identical fields), and cross-references between drawers (foreign keys) are checked so nothing dangles. You can pull and recombine forms in ways nobody planned — that's the join.

-- The relational model you already know: structured tables.
-- A "users" table with typed columns, plus a related "orders"
-- table linked by a foreign key. Joins reassemble the picture.
CREATE TABLE users (
    id    INTEGER PRIMARY KEY,
    name  TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
);

CREATE TABLE orders (
    id       INTEGER PRIMARY KEY,
    user_id  INTEGER REFERENCES users(id),  -- the relationship
    total    NUMERIC NOT NULL
);

-- Every order belongs to exactly one user; the schema enforces it.
-- This is schema-on-write: a bad shape is rejected at INSERT time.
-- The relational superpower: ad-hoc joins across normalized tables.
-- "Each user with their total spend" — assembled at query time.
SELECT u.name,
       COUNT(o.id)       AS order_count,
       SUM(o.total)      AS lifetime_value
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.name
ORDER BY lifetime_value DESC;

-- You did NOT have to anticipate this query when designing the
-- schema. Normalization + joins let you ask new questions freely.
-- That flexibility is exactly what most NoSQL stores trade away.

-- ✅ Expected result:
-- name | order_count | lifetime_value

2. Document Stores — MongoDB

A document database stores semi-structured documents (JSON/BSON-like) grouped into collections. Related data is often embedded in one document, so reading an entity is a single lookup with no join. Documents in a collection need not share a shape — that's schema-on-read. MongoDB is the canonical example.

Content management, product catalogs, user profiles, event logs — anywhere the data is naturally hierarchical and you usually fetch a whole entity at once. Less ideal when you need many-to-many joins or transactions spanning lots of entities.

-- DOCUMENT model (e.g. MongoDB). Illustrative — NOT runnable here.
-- The same user, but their orders are EMBEDDED in one document.
-- A "collection" of documents replaces the table; each doc is JSON-like.

-- db.users.insertOne({
--   _id: 1,
--   name: "Ada",
--   email: "ada@example.com",
--   orders: [                      // embedded, denormalized
--     { id: 101, total: 49.99 },
--     { id: 102, total: 19.50 }
--   ]
-- })

-- Reading one user returns everything in a single lookup — no join.
-- Trade-off: if an order's shape must change, you touch many docs,
-- and there is no engine-enforced link between users and orders.

3. Key-Value Stores — Redis

A key-value store is the simplest model: unique keys mapped to values, fetched by key. Redis keeps data primarily in memory, making reads and writes extremely fast. There are no joins and no querying by value — you must know the key.

Caching, session storage, rate limiting, real-time counters, and leaderboards. Often sits alongside a relational database as a fast cache, not as the system of record.

-- KEY-VALUE model (e.g. Redis). Illustrative — NOT runnable here.
-- Just unique keys mapped to values. Blisteringly fast, very simple.

-- SET user:1:name      "Ada"
-- SET user:1:email     "ada@example.com"
-- INCR user:1:visits                 -- atomic counter
-- EXPIRE session:abc123  3600        -- auto-expiring session

-- GET user:1:name   ->   "Ada"

-- There are no queries "by value" and no joins: you fetch by key.
-- Perfect for caching, sessions, rate limits, leaderboards.
-- Wrong tool the moment you need to search or relate data.

4. Wide-Column Stores — Cassandra

A wide-column store organizes data into rows that share a partition key but can hold different columns. Apache Cassandra distributes those partitions across many nodes, giving enormous write throughput and linear horizontal scaling. The catch: you design tables around the queries you need, because efficient reads must hit the partition key.

Unlike relational normalization, here you denormalize and duplicate data into one table per access pattern. Flexible ad-hoc querying is gone; in return you get write scale that relational single-node setups can't match.

-- WIDE-COLUMN model (e.g. Cassandra). Illustrative — NOT runnable here.
-- Rows keyed by a partition key; columns can vary row to row.
-- You design the table AROUND the query you must serve.

-- CREATE TABLE orders_by_user (
--   user_id   int,
--   order_id  int,
--   total     decimal,
--   PRIMARY KEY (user_id, order_id)   -- partition by user_id
-- );

-- SELECT * FROM orders_by_user WHERE user_id = 1;   -- fast, single partition

-- Scales horizontally to huge write volumes across many nodes.
-- Cost: queries not matching the partition key are slow or disallowed.

5. Graph Databases — Neo4j

A graph database models data as nodes connected by typed edges, where relationships are first-class citizens. Neo4j (queried with Cypher) makes multi-hop traversals — friends-of-friends, shortest paths, recommendation chains — fast and natural, whereas the same questions become deeply nested self-joins in SQL.

Social networks, recommendation engines, fraud-ring detection, knowledge graphs, and network/IT topology — any domain where the connections matter as much as the entities.

-- GRAPH model (e.g. Neo4j, Cypher syntax). Illustrative — NOT runnable here.
-- Data is NODES connected by typed EDGES. Relationships are first-class.

-- (Ada:User)-[:PLACED]->(o1:Order {total: 49.99})
-- (Ada:User)-[:FOLLOWS]->(Grace:User)

-- "Friends-of-friends who bought product X":
-- MATCH (me:User)-[:FOLLOWS*2]->(fof)-[:PLACED]->(:Order)-[:CONTAINS]->(p:Product {name:'X'})
-- RETURN DISTINCT fof.name;

-- Multi-hop traversals like this are awkward joins in SQL but
-- natural here. Shine for social graphs, recommendations, fraud rings.

6. ACID vs BASE, and the CAP Theorem

ACID (Atomicity, Consistency, Isolation, Durability) is the strong guarantee relational transactions give: changes are all-or-nothing, valid, isolated from each other, and durable once committed. Many distributed NoSQL systems instead lean on BASE (Basically Available, Soft state, Eventual consistency): they stay available and converge to a consistent state over time, accepting brief inconsistency for scale.

The CAP theorem explains why. In a distributed system you cannot, during a network partition (P), have both perfect consistency (C) and full availability (A) — you must sacrifice one. A CP system (like a strongly-consistent config store) refuses some requests to stay correct; an AP system (like Cassandra in its default tuning) keeps answering and reconciles later. It is a spectrum and a tunable choice, not a rigid label.

7. Schema, Normalization & Horizontal Scaling

Schema-on-write (relational) validates structure when you insert, catching bad data early and keeping queries predictable. Schema-on-read (many NoSQL) accepts whatever you write and applies structure when reading, which is flexible but pushes the burden of consistency onto the application.

Normalization splits data into related tables to remove redundancy — ideal when you query flexibly and want a single source of truth. Denormalization deliberately duplicates data to serve a known read pattern fast, which is the default mindset in document and wide-column stores. Neither is "better"; they optimize for different things.

Relational databases traditionally scale vertically (a bigger machine) and scale reads with replicas; pushing writes beyond one node means sharding, which adds real complexity. Many NoSQL systems were built to scale horizontally from the start, spreading data across commodity nodes via automatic sharding — that built-in write-scaling is often their main draw.

Your Turn: the join NoSQL would denormalize away

Fill in the blanks to count each user's orders with a join. This is precisely the flexible query a document store trades away by embedding — and the relational model hands it to you for free.

-- 🎯 YOUR TURN — fill in the blanks, then press "Try it Yourself"
-- Goal: list each user with how many orders they placed.
-- (This is the kind of ad-hoc join relational DBs make easy.)

SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.___    -- 👉 the column orders points back to
GROUP BY u.___                             -- 👉 group so it is one row per user
ORDER BY order_count DESC;

-- ✅ Expected: every user with a count of their orders, busiest first.

Common Misconceptions (and the reality)

Frequently Asked Questions

Q: What should my default database be?

A relational database (PostgreSQL is an excellent default). It gives you ACID transactions, ad-hoc querying with joins, mature tooling, and the flexibility to answer questions you didn't foresee. Reach for NoSQL when a concrete requirement — scale, shape, or access pattern — justifies it.

Q: What is polyglot persistence?

Using multiple database types in one system, each for what it does best: the relational store as the source of truth, a key-value cache for hot reads, a document store for flexible content, a graph for relationships, and a search engine for full-text. Real systems are often a mix.

Q: Does NoSQL really have no joins?

Most NoSQL stores either lack joins or make them expensive, so you either embed/denormalize the related data or join in application code. Graph databases are the exception: traversing relationships is exactly what they optimize for.

Q: ACID or BASE — which do I need?

If correctness of every transaction matters (payments, inventory, accounting), favor ACID and a relational database. If availability and scale matter more and brief inconsistency is tolerable (activity feeds, view counts), BASE-style eventual consistency can be the right trade.

🎉 Lesson Complete

Practice quiz

What is the defining feature of the relational model that NoSQL stores often relax?

  • A fixed schema of tables, rows, and columns with relationships
  • The use of SQL keywords
  • Storing data on disk
  • Returning JSON to clients

Answer: A fixed schema of tables, rows, and columns with relationships. Relational databases enforce a structured schema of tables with typed columns and explicit relationships; many NoSQL stores relax that rigid structure.

Which NoSQL category is MongoDB the classic example of?

  • Graph database
  • Key-value store
  • Document store
  • Wide-column store

Answer: Document store. MongoDB is a document database: it stores semi-structured documents (BSON/JSON-like) grouped into collections.

Redis is best described as which kind of store?

  • A relational database
  • An in-memory key-value store
  • A wide-column store
  • A graph database

Answer: An in-memory key-value store. Redis maps unique keys to values and keeps data primarily in memory, making it ideal for caching, sessions, and counters.

Apache Cassandra is an example of which NoSQL category?

  • Graph database
  • Document store
  • Key-value store
  • Wide-column store

Answer: Wide-column store. Cassandra is a wide-column store: rows can have varying columns, organized into column families and partitioned across many nodes.

Neo4j is purpose-built for which type of data?

  • Highly connected data modeled as nodes and edges
  • Flat tabular reports
  • Simple cache lookups
  • Large binary blobs

Answer: Highly connected data modeled as nodes and edges. Neo4j is a graph database, optimized for traversing relationships between nodes (vertices) connected by edges.

What does the A in the ACID guarantees stand for?

  • Aggregation
  • Atomicity
  • Availability
  • Asynchronous

Answer: Atomicity. ACID stands for Atomicity, Consistency, Isolation, and Durability. Atomicity means a transaction either fully completes or fully rolls back.

BASE is an alternative to ACID often associated with NoSQL. What does it emphasize?

  • Strong consistency above all
  • Binary And Structured Encoding
  • Batch And Sequential Execution
  • Basically Available, Soft state, Eventual consistency

Answer: Basically Available, Soft state, Eventual consistency. BASE favors Basically Available, Soft state, and Eventual consistency, trading immediate consistency for availability and scale.

According to the CAP theorem, during a network partition a distributed system must choose between which two properties?

  • Speed and Storage
  • Indexing and Sharding
  • Consistency and Availability
  • Atomicity and Durability

Answer: Consistency and Availability. CAP says that when a partition (P) occurs, a system can guarantee either Consistency or Availability, but not both at the same time.

What does schema-on-read mean compared to schema-on-write?

  • The schema is enforced when data is written
  • Structure is applied when the data is read/queried, not when stored
  • There is never any structure at all
  • The schema is stored in a separate file

Answer: Structure is applied when the data is read/queried, not when stored. Schema-on-read defers structure to query time, so the store accepts varied shapes; schema-on-write (relational) validates structure when inserting.

When is a relational database still the right default choice?

  • For most applications needing strong consistency, transactions, and ad-hoc queries with joins
  • Only for graph traversal workloads
  • Never, NoSQL has fully replaced it
  • Only for data smaller than one megabyte

Answer: For most applications needing strong consistency, transactions, and ad-hoc queries with joins. Relational databases remain the sensible default for most apps: mature tooling, ACID transactions, flexible ad-hoc querying, and joins. Reach for NoSQL when a specific need justifies it.

Continue this course

Related lessons