Database comparison

SQL vs NoSQL

The "which database?" question is one of the most repeated architectural debates in IT. The right answer almost always depends on access patterns, not on which family is "modern" or "fast".

TL;DR

  • SQL (relational) — tables, fixed schema, joins, ACID transactions. Examples: PostgreSQL, MySQL, SQL Server, SQLite.
  • NoSQL — an umbrella for four families: document (MongoDB), key-value (Redis, DynamoDB), wide-column (Cassandra), and graph (Neo4j). Flexible schema, easier horizontal scaling.
  • Default to relational. Move to NoSQL only when you have a concrete reason: scale, schema flexibility, or a well-fitting data model.

Side-by-side comparison

AspectSQL (Relational)NoSQL
Data modelTables, rows, columnsDocuments, key-value, wide-column, or graph
SchemaStrict, enforced by DBFlexible, often enforced by app
Query languageSQL (standardised)Varies — JSON queries, custom DSLs, key lookups
JoinsFirst-classDiscouraged; denormalise instead
TransactionsACID by default, multi-rowVaries; often limited to single document/partition
ScalingVertical first; sharding is complexHorizontal-first; designed for sharding
ConsistencyStrong by defaultOften eventual (configurable)
Use casesBusiness systems, financial, CRUD appsCatalogs, sessions, time-series, search, caching
ExamplesPostgreSQL, MySQL, SQL Server, OracleMongoDB, DynamoDB, Cassandra, Redis, Elasticsearch, Neo4j

Code side-by-side

Find all orders over $100 placed in the last 7 days by user 42:

SQL (PostgreSQL)

SELECT id, total, created_at
FROM orders
WHERE user_id = 42
  AND total > 100
  AND created_at >= NOW() - INTERVAL '7 days'
ORDER BY created_at DESC;

NoSQL (MongoDB)

db.orders.find({
  user_id: 42,
  total: { $gt: 100 },
  created_at: {
    $gte: new Date(Date.now() - 7*24*60*60*1000)
  }
}).sort({ created_at: -1 });

When to use SQL

  • Highly relational data. Users have orders, orders have line items, line items reference products — relational tables map cleanly.
  • Complex queries and reporting. Ad-hoc JOINs, GROUP BY, window functions, analytical aggregations.
  • Strong consistency / transactions required. Banking, e-commerce checkout, inventory.
  • Schema evolves predictably. Migrations via Liquibase / Flyway / Alembic are well-tooled.
  • Team default skill. SQL is universal; no surprise interview questions.

When to use NoSQL

  • Massive scale with known access patterns. DynamoDB for global low-latency key lookups, Cassandra for write-heavy time-series.
  • Document-shaped data. Variable-shape user profiles, product catalogs with per-category attributes.
  • Caching. Redis for session storage, query caching, rate limiting.
  • Full-text search. Elasticsearch / OpenSearch — relational full-text is workable but specialised tools win.
  • Graph traversal. Neo4j for social networks, recommendation engines, fraud detection.

English phrases engineers use

SQL conversations

  • "We need a join between orders and users."
  • "This query is doing a full table scan — let's add an index on user_id."
  • "Wrap the writes in a transaction — we don't want partial state."
  • "The migration adds a NOT NULL constraint — we need a default for existing rows."
  • "That's N+1 — the ORM is firing a query per parent row."

NoSQL conversations

  • "We denormalised the user's profile into the document."
  • "The partition key is the user ID — all their data lives on one node."
  • "This query has a hot partition — one shard is taking all the traffic."
  • "Eventual consistency means the read may show stale data for a few seconds."
  • "We need to backfill the new field across all existing documents."

Quick decision tree

  • Building a typical CRUD application → Relational (Postgres)
  • Need ACID across many rows → Relational
  • Caching layer in front of any DB → Redis
  • Full-text search central to UX → Elasticsearch / OpenSearch
  • Graph relationships dominate (social, recommendations) → Neo4j
  • Massive write throughput with known queries → Cassandra / DynamoDB
  • Variable-shape documents (catalog) → MongoDB or Postgres JSONB
  • When in doubt → Postgres with JSONB columns covers ~80% of cases

Common Mistakes and Trade-offs

A remarkably common mistake among teams adopting NoSQL databases, particularly MongoDB or Cassandra, is assuming eventual consistency equates to acceptable performance across the board. The 'eventual' in eventual consistency refers to a period where data may be slightly out of sync across replicas before converging. This isn't simply a theoretical concern; developers often underestimate the impact on read-heavy applications without explicitly designing for this state. Ignoring strategies like conflict resolution, careful query design focused on idempotent operations, and monitoring data divergence leads to unpredictable behavior and frustrated users – especially in systems requiring strong transactional guarantees. The assumption that 'it's good enough' is a recipe for disaster at scale.

Scaling NoSQL databases often exposes significant operational trade-offs related to data modeling. While SQL databases traditionally rely on normalized schemas minimizing redundancy, many NoSQL solutions—especially document stores—promote denormalization to optimize read performance. However, this comes with a massive increase in update complexity and potential for data inconsistencies if not managed meticulously. At scale, the overhead of coordinating updates across multiple documents becomes substantial, often requiring sophisticated techniques like optimistic locking and careful versioning strategies that can quickly become as complex – or more so – than managing a normalized relational schema. The perceived simplicity of denormalization masks a significant operational burden.

The misconception that SQL databases are inherently 'always better' for structured data ignores the rise of powerful, mature NoSQL solutions capable of handling complex relationships and large datasets efficiently. While SQL excels in scenarios demanding strict ACID compliance and well-defined schemas – like financial transactions or inventory management – many modern applications benefit from the flexibility and scalability of graph databases (like Neo4j) or wide-column stores (like Cassandra). Furthermore, a relational database isn't automatically better just because it has more features; its rigid structure can severely limit innovation and adaptability in rapidly evolving domains. The choice should be driven by requirements, not preconceived notions.

Migrating between SQL and NoSQL systems is rarely a simple lift-and-shift operation. A common pitfall is attempting to directly map relational schemas onto NoSQL data models without considering the fundamental differences in how each approach handles relationships. More critically, the migration process often reveals hidden dependencies within the application logic that were previously masked by the constraints of the relational model. Furthermore, a hybrid architecture – combining SQL and NoSQL – can introduce significant complexity in terms of data synchronization, query orchestration, and potential performance bottlenecks if not carefully designed with distributed transaction management techniques or careful schema evolution strategies in mind.

Frequently asked questions

What is the main difference between SQL and NoSQL databases in plain English?

SQL databases store data in tables with rows and columns, enforce a fixed schema, and use the SQL query language. NoSQL is an umbrella term for databases that do not use the relational table model — they store documents, key-value pairs, wide columns, or graphs and typically have flexible or no schema enforcement.

Is NoSQL faster than SQL?

Not inherently. NoSQL systems can scale horizontally (across many machines) more easily, which can produce higher throughput for some workloads. But a properly-tuned PostgreSQL instance routinely outperforms misconfigured NoSQL. Performance depends far more on data model, indexes, and access patterns than on the SQL/NoSQL label.

When should I pick a relational database?

Pick relational (Postgres, MySQL, SQL Server) when your data is genuinely relational (entities with foreign keys), when you need ACID transactions across multiple rows, when ad-hoc analytical queries matter, and when your team already knows SQL. The default choice for most CRUD applications should still be a relational database.