NoSQL vs SQL Cheat Sheet
Compares relational and NoSQL databases across schema flexibility, consistency guarantees, scaling model, and query capability to guide database selection.
SQL vs NoSQL: Core Differences
The main axes relational and NoSQL databases differ on.
- Schema- SQL enforces a fixed schema at write time; NoSQL is typically schema-less or schema-on-read, allowing flexible/evolving documents
- Data model- SQL uses normalized tables and rows; NoSQL uses documents, key-value pairs, wide columns, or graphs depending on the database
- Consistency- SQL databases default to strong (ACID) consistency; many NoSQL stores favor eventual consistency for higher availability and throughput
- Scaling- SQL traditionally scales vertically (bigger server) or via careful sharding; NoSQL is generally designed for horizontal scaling out of the box
- Joins- SQL supports joins natively; NoSQL generally avoids joins in favor of denormalization or embedding related data
- Transactions- SQL supports multi-row, multi-table ACID transactions; NoSQL transaction support varies (e.g., MongoDB supports multi-document ACID transactions since v4.0)
NoSQL Database Types
The major categories of NoSQL data stores.
- Document store- Stores JSON/BSON-like documents (e.g., MongoDB, Couchbase); good for nested, evolving records
- Key-value store- Simple key -> value lookups (e.g., Redis, DynamoDB); extremely fast for caching and session data
- Wide-column store- Rows with dynamic columns grouped into column families (e.g., Cassandra, HBase); good for time-series and write-heavy workloads
- Graph database- Stores nodes and edges optimized for traversing relationships (e.g., Neo4j); good for social graphs, recommendations, fraud detection
Equivalent Query Comparison
The same lookup expressed relationally and as a document query.
// SQL: find pending orders for a customer, joined with items// SELECT o.id, o.total FROM orders o// WHERE o.customer_id = 42 AND o.status = 'pending';// MongoDB: same lookup on a document collectiondb.orders.find( { customer_id: 42, status: "pending" }, { _id: 1, total: 1 });// MongoDB embeds items directly instead of a JOINdb.orders.findOne({ _id: 42 }).items; // array embedded in the doc
When to Use Which
Rules of thumb for picking a database family.
- Choose SQL when- Data is highly relational, you need multi-table transactions, complex ad-hoc queries, or strong consistency (e.g., financial ledgers)
- Choose document DB when- Data is naturally hierarchical/nested and access patterns are known upfront (e.g., product catalogs, user profiles, CMS content)
- Choose key-value when- You need sub-millisecond lookups by a single key (e.g., session storage, caching, feature flags)
- Choose wide-column when- You have massive write throughput and time-ordered data across many nodes (e.g., IoT telemetry, event logging)
- Choose graph DB when- The relationships between entities are the primary query target (e.g., "friends of friends", fraud rings)
CAP Theorem in Practice
How real databases position themselves on the consistency/availability tradeoff during a network partition.
// CAP theorem: during a network Partition, pick Consistency or Availability// (Partition tolerance is assumed mandatory in a distributed system)// CP systems: refuse writes/reads on the minority side to stay consistent// - MongoDB (with majority write/read concern), HBase, Redis Cluster (default)// AP systems: keep serving reads/writes on both sides, reconcile later// - Cassandra (tunable), DynamoDB (eventually consistent reads), Couchbase// Tunable consistency example: Cassandra read/write consistency levels// WRITE with QUORUM, READ with QUORUM -> strong consistency (R + W > N)// WRITE with ONE, READ with ONE -> higher availability, eventual consistencyconst query = "INSERT INTO orders (id, status) VALUES (?, ?) USING CONSISTENCY QUORUM;";
Multi-Document Transactions & Schema Validation
NoSQL doesn't mean no rules — MongoDB supports ACID transactions and JSON Schema validation.
// Multi-document ACID transaction across two collectionsconst session = client.startSession();try { session.startTransaction({ readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } }); await accounts.updateOne({ _id: fromId }, { $inc: { balance: -amount } }, { session }); await accounts.updateOne({ _id: toId }, { $inc: { balance: amount } }, { session }); await session.commitTransaction();} catch (e) { await session.abortTransaction(); throw e;} finally { session.endSession();}// Enforce structure even in a 'schema-less' storedb.createCollection("orders", { validator: { $jsonSchema: { required: ["customer_id", "status"], properties: { status: { enum: ["pending", "paid", "shipped"] } } } }});
Consistency Models Beyond Strong/Eventual
The spectrum of consistency guarantees distributed databases actually offer.
- Strong consistency- Every read sees the latest committed write immediately, regardless of which replica serves it (e.g., single-leader SQL replicas with sync replication)
- Eventual consistency- Replicas converge to the same value given enough time with no new writes; reads may return stale data in the meantime (e.g., DynamoDB default reads)
- Read-your-writes consistency- A client is guaranteed to see its own prior writes, even if other clients might not yet (common session-level guarantee)
- Monotonic reads- Once a client has seen a value, subsequent reads never return an older value — prevents time appearing to go backwards
- Causal consistency- Operations that are causally related are seen by all nodes in the same order; concurrent unrelated ops may be seen in different orders
- Tunable consistency- Systems like Cassandra/ScyllaDB let each query choose its consistency level (ONE, QUORUM, ALL) trading latency for correctness per-operation
Denormalization Patterns for Document Stores
Common access-pattern-driven modeling strategies that replace joins in NoSQL.
// 1. Embedding: bounded, always-accessed-together data lives in the parent doc{ _id: 1, name: "Order #1", items: [{ sku: "A1", qty: 2 }, { sku: "B2", qty: 1 }] }// 2. Referencing: large or independently-accessed data stays separate{ _id: 1, customer_id: 42 } // look up customers collection separately// 3. Extended reference (denormalized copy): duplicate a few hot fields// to avoid a second round trip, accepting eventual staleness{ _id: 1, customer_id: 42, customer_name: "Jane Doe" /* denormalized */ }// 4. Bucket pattern: group high-volume time-series events into fixed buckets// instead of one document per event, cutting document count drastically{ sensor_id: "s1", date: "2026-07-21", readings: [ [0, 21.5], [60, 21.7], [120, 21.6] ] }
Hybrid & Multi-Model Reality
The SQL/NoSQL line has blurred — modern engines borrow from both worlds.
- JSONB in Postgres- Binary JSON column type with GIN indexing lets Postgres serve document-style queries with full SQL and ACID guarantees
- NewSQL- Systems like CockroachDB, YugabyteDB, and Google Spanner offer horizontal scaling and distributed consensus while keeping SQL and ACID transactions
- DynamoDB single-table design- Modeling multiple entity types in one table using composite partition/sort keys to satisfy all access patterns with minimal round trips
- Change data capture (CDC)- Tools like Debezium stream row-level changes from an SQL database into Kafka, feeding NoSQL read models or search indexes (CQRS-style)
- Polyglot persistence- Using different databases for different subsystems of the same application (e.g., Postgres for orders, Redis for sessions, Elasticsearch for search)
Don't pick NoSQL purely for 'web scale' — plenty of SQL databases (Postgres, MySQL, CockroachDB) now handle massive horizontal scale and JSON columns; choose based on your actual consistency and query-pattern requirements, not hype.