SQL vs NoSQL: How to Choose the Right Database
Compare SQL and NoSQL databases by data model, consistency, scalability, and use case. Learn when to use PostgreSQL, MongoDB, Redis, or Cassandra.
- sql
- nosql
- database
- postgresql
- mongodb
SQL and NoSQL databases solve different problems. The choice depends on your data structure, consistency requirements, query patterns, and scale. This guide cuts through the marketing and gives you a practical framework.
What SQL databases are
SQL databases (relational databases) store data in tables with rows and columns. They enforce a fixed schema and use SQL as the query language. Relationships between tables are expressed through foreign keys and enforced by the database.
Examples: PostgreSQL, MySQL, SQLite, SQL Server, Oracle.
-- Schema-enforced structure
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(user_id),
total NUMERIC(10, 2) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Query across related tables
SELECT
u.email,
COUNT(o.order_id) AS order_count,
SUM(o.total) AS lifetime_value
FROM users AS u
LEFT JOIN orders AS o ON u.user_id = o.user_id
GROUP BY u.user_id, u.email
ORDER BY lifetime_value DESC;
What NoSQL databases are
NoSQL databases don’t require a fixed schema or relational structure. “NoSQL” covers several different data models:
| Type | Model | Examples |
|---|---|---|
| Document | JSON-like documents | MongoDB, CouchDB, Firestore |
| Key-value | Plain key → value | Redis, DynamoDB, Memcached |
| Wide-column | Rows + dynamic columns | Cassandra, HBase |
| Graph | Nodes + edges | Neo4j, Amazon Neptune |
| Time-series | Timestamped measurements | InfluxDB, TimescaleDB |
// MongoDB document — no schema enforced by default
{
"_id": "user_123",
"email": "alice@example.com",
"orders": [
{ "order_id": "ord_1", "total": 49.99, "items": [...] },
{ "order_id": "ord_2", "total": 89.00, "items": [...] }
],
"preferences": {
"theme": "dark",
"notifications": true
}
}
The core trade-offs
Schema flexibility
SQL: Schema is defined upfront. Adding a column requires a migration. The database rejects data that doesn’t fit the schema.
NoSQL (document): Documents in the same collection can have different fields. No migration needed for new fields. The application must handle missing fields.
# SQL: migration required to add a field
# ALTER TABLE users ADD COLUMN referral_code TEXT;
# MongoDB: just write the new field — existing documents won't have it
db.users.update_one(
{"_id": user_id},
{"$set": {"referral_code": "ALICE2026"}}
)
Query flexibility
SQL: Ad-hoc queries across any combination of tables and columns. JOINs, aggregations, window functions, CTEs — all built in.
NoSQL: Query patterns are often determined at design time. MongoDB has an aggregation pipeline; key-value stores like Redis have almost no query capability beyond key lookup.
-- SQL: complex ad-hoc query is straightforward
SELECT
p.category,
DATE_TRUNC('month', o.created_at) AS month,
SUM(oi.quantity * oi.unit_price) AS revenue
FROM order_items AS oi
JOIN orders AS o ON oi.order_id = o.order_id
JOIN products AS p ON oi.product_id = p.product_id
WHERE o.status = 'completed'
GROUP BY p.category, month
ORDER BY month DESC, revenue DESC;
// MongoDB: same query requires a multi-stage aggregation pipeline
db.order_items.aggregate([
{ $lookup: { from: "orders", localField: "order_id", foreignField: "_id", as: "order" } },
{ $unwind: "$order" },
{ $match: { "order.status": "completed" } },
// ... several more stages
])
Consistency (ACID vs. BASE)
SQL databases provide ACID guarantees:
- Atomicity — a transaction either fully commits or fully rolls back
- Consistency — data is always in a valid state
- Isolation — concurrent transactions don’t interfere
- Durability — committed data survives crashes
-- SQL: atomic transfer — both updates succeed or both fail
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT;
Many NoSQL databases follow BASE:
- Basically Available — reads/writes are available even during failures
- Soft state — data may be inconsistent between replicas for a period
- Eventually consistent — replicas will converge to the same value
Redis Cluster and Cassandra prioritize availability over strict consistency. MongoDB supports multi-document ACID transactions since v4.0, but with performance overhead.
Scalability
SQL vertical scaling (bigger server) is straightforward. Horizontal scaling (sharding across servers) is possible but complex. PostgreSQL and MySQL have mature solutions (Citus, Vitess) but require significant operational effort.
NoSQL horizontal scaling is often the primary selling point. Cassandra, MongoDB, and DynamoDB are designed to distribute across many nodes with minimal configuration.
When to choose SQL
- Relational data with many entities that reference each other (users → orders → products → reviews)
- Complex queries that aren’t known at design time — analytics, reporting, ad-hoc analysis
- Strong consistency requirements — financial transactions, inventory, reservations
- Mature ecosystem — PostgreSQL has decades of tooling, extensions (PostGIS, pgvector), and operational expertise
- You’re not sure yet — SQL is more flexible for pivoting query patterns
PostgreSQL handles most use cases well. It supports JSON/JSONB for document-style storage, arrays, full-text search, and geospatial queries. Many applications that “need NoSQL” are actually fine on Postgres.
When to choose NoSQL
Document (MongoDB, Firestore):
- Data is naturally document-shaped with variable fields
- Nested objects that are always queried together (a blog post with its comments)
- Content management, product catalogs, user profiles
Key-value (Redis):
- Caching with TTLs
- Session storage
- Real-time leaderboards (sorted sets)
- Pub/sub messaging
Wide-column (Cassandra):
- Write-heavy workloads at massive scale (IoT telemetry, activity streams)
- Data is always accessed by a known partition key
- Geographic distribution is required
Time-series (InfluxDB, TimescaleDB):
- Metrics, monitoring data, sensor readings
- Queries are always time-bounded
- High write throughput with time-ordered data
Graph (Neo4j):
- Relationship traversal is the primary query pattern
- Social networks, fraud detection, recommendation engines
The polyglot pattern
Many production systems use multiple databases:
- PostgreSQL — primary data store for transactional data
- Redis — cache layer, sessions, rate limiting
- Elasticsearch — full-text search
- ClickHouse or BigQuery — analytics on large datasets
This is normal. Start with PostgreSQL. Add specialized stores only when you have a clear need they solve better than Postgres extensions.
Quick decision guide
| Situation | Choose |
|---|---|
| Relational data with JOINs | SQL (PostgreSQL) |
| Flexible schema, nested documents | MongoDB |
| Caching, sessions, pub/sub | Redis |
| Write-heavy IoT/telemetry at scale | Cassandra |
| Full-text search | Elasticsearch or Postgres FTS |
| Analytics on billions of rows | ClickHouse, BigQuery |
| Graph traversal | Neo4j |
| Mobile/web apps with offline sync | Firestore |
| Don’t know yet | PostgreSQL |
Format your SQL queries at sqlformat.io.
Related reading
-
SQL Joins Explained: INNER, LEFT, RIGHT, FULL, and CROSS
A visual and practical guide to SQL joins: INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, and CROSS JOIN — with examples for each type.
-
SQL EXPLAIN and Execution Plans: A Complete Guide
Learn to read SQL execution plans using EXPLAIN and EXPLAIN ANALYZE in PostgreSQL and MySQL. Identify seq scans, index scans, hash joins, and cost estimates.
-
SQL Query Optimization: Practical Techniques That Actually Work
Speed up slow SQL queries using indexes, avoiding SELECT *, rewriting subqueries, analyzing execution plans, and common anti-patterns to eliminate.