# SQL vs NoSQL: як пояснити вибір бази даних на співбесіді

> Реляційна і документна моделі на одному прикладі: замовлення в PostgreSQL і MongoDB, критерії вибору, типові міфи, ACID простими словами і формулювання сильної відповіді на співбесіді.

- Автор: Юра Скиба (https://cookiesoftware.io)
- Опубліковано: 2026-09-24
- Категорія: Backend і System Design
- Canonical: https://cookiesoftware.io/blog/sql-vs-nosql

---

**Коротка відповідь.** Питання «SQL чи NoSQL» на співбесіді перевіряє не знання назв, а вміння міркувати від даних: які зв'язки між сутностями, наскільки важлива консистентність, як читається і пишеться інформація. Сильна відповідь завжди починається з «залежить від задачі» і продовжується конкретними критеріями. Слабка – з «NoSQL швидший». Нижче – один приклад у двох моделях і готова рамка аргументації.

## Одна задача – дві моделі

Візьмемо магазин: користувачі, товари, замовлення. Замовлення містить кілька товарів із кількістю.

### Реляційна модель (PostgreSQL)

Дані нормалізовані: кожна сутність у своїй таблиці, зв'язки через зовнішні ключі:

```sql
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT NOT NULL UNIQUE
);

CREATE TABLE products (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title TEXT NOT NULL,
  price_cents INTEGER NOT NULL CHECK (price_cents >= 0)
);

CREATE TABLE orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES users(id),
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE order_items (
  order_id UUID NOT NULL REFERENCES orders(id),
  product_id UUID NOT NULL REFERENCES products(id),
  quantity INTEGER NOT NULL CHECK (quantity > 0),
  PRIMARY KEY (order_id, product_id)
);
```

«Усі замовлення користувача з товарами» – це JOIN:

```sql
SELECT o.id, o.created_at, p.title, oi.quantity
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
JOIN products p ON p.id = oi.product_id
WHERE o.user_id = $1
ORDER BY o.created_at DESC;
```

Що дає модель: база сама гарантує цілісність (не можна створити замовлення неіснуючого користувача), а нові способи читання даних не вимагають зміни схеми.

### Документна модель (MongoDB)

Замовлення – один документ, у який вкладено товари:

```ts
// Колекція orders
{
  _id: ObjectId('...'),
  userId: ObjectId('...'),
  createdAt: ISODate('2026-09-24T10:00:00Z'),
  items: [
    { productId: ObjectId('...'), title: 'Клавіатура', priceCents: 250000, quantity: 1 },
    { productId: ObjectId('...'), title: 'Кабель USB-C', priceCents: 40000, quantity: 2 },
  ],
}
```

Читання замовлення – один запит без JOIN, і це головна сила: документ зібраний так, як його споживає застосунок. Ціна: назва товару скопійована в документ. Якщо товар перейменували, старі замовлення зберігають стару назву – для історії замовлень це навіть правильно, але це РІШЕННЯ, яке треба ухвалити свідомо, а не отримати випадково.

## Критерії вибору

| Критерій | Схиляє до SQL | Схиляє до NoSQL (документи) |
|---|---|---|
| Зв'язки | Багато зв'язків «багато-до-багатьох», запити з різних боків | Дані читаються цілісними агрегатами |
| Консистентність | Критична (гроші, залишки, бронювання) | Прийнятна eventual consistency |
| Схема | Стабільна, спільна для команд | Часто змінюється, різнорідні об'єкти |
| Запити | Заздалегідь невідомі, аналітика, звіти | Відомі наперед, прості за ключем |
| Транзакції | Між кількома сутностями регулярно | Здебільшого в межах одного документа |

І чесне доповнення для співбесіди: у більшості продуктових задач PostgreSQL – розумний вибір за замовчуванням, бо покриває і JSON-поля, і повнотекстовий пошук, і горизонтальне читання через репліки. NoSQL обирають під конкретний профіль навантаження, а не «на всяк випадок».

## ACID простими словами

- **Atomicity** – транзакція виконується повністю або не виконується взагалі. Списання коштів і створення замовлення не можуть «розійтися».
- **Consistency** – після транзакції дані відповідають правилам схеми (унікальність, зовнішні ключі, CHECK).
- **Isolation** – паралельні транзакції не бачать проміжних станів одна одної. Рівні ізоляції – компроміс між строгістю і швидкістю.
- **Durability** – закомічені дані переживають падіння сервера.

Практичний приклад для відповіді: два покупці одночасно купують останню одиницю товару. Без ізоляції обидва побачать «є 1 шт» і обидва оформлять замовлення. Транзакція з блокуванням рядка або перевіркою залишку в UPDATE вирішує це на рівні бази.

## Типові міфи

- **«NoSQL швидший».** Швидшим є читання одного документа без JOIN. Але запит «усі замовлення з товаром X за місяць» у документній моделі може вимагати сканування всієї колекції. Швидкість залежить від відповідності моделі й запитів, а не від класу бази.
- **«SQL не масштабується».** Реляційні бази масштабуються реплікацією на читання і шардингом; величезні продукти живуть на PostgreSQL і MySQL. Складнішим є саме шардинг із транзакціями між шардами, і чесно назвати це обмеження – плюс на співбесіді.
- **«У NoSQL немає транзакцій».** У MongoDB транзакції на кілька документів існують; але модель даних зазвичай будують так, щоб вони були рідкістю. Деталі – в [офіційній документації MongoDB](https://www.mongodb.com/docs/).
- **«Схема в NoSQL не потрібна».** Схема є завжди, питання лише де: у базі чи в коді застосунку. «Schemaless» означає, що помилки схеми виявляться в runtime.

## А що з JSON у PostgreSQL?

Окреме питання, яке любить середина співбесіди: «навіщо тоді NoSQL, якщо в PostgreSQL є JSONB?» Чесна відповідь: JSONB закриває значну частину кейсів «гнучкої» частини даних – атрибути товарів різних типів, налаштування користувача, сирі payload-и вебхуків. Можна індексувати поля всередині JSON і поєднувати їх зі звичайними реляційними колонками:

```sql
CREATE TABLE products (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title TEXT NOT NULL,
  attributes JSONB NOT NULL DEFAULT '{}'
);

-- Індекс під пошук за атрибутом
CREATE INDEX idx_products_brand ON products ((attributes->>'brand'));
```

Це не робить документні бази непотрібними, але ще раз підтверджує правило за замовчуванням: почни з PostgreSQL, ускладнюй інфраструктуру лише під виміряну потребу.

## Сильна vs слабка відповідь

**Питання:** «Яку базу ти б обрав для сервісу замовлень?»

**Слабко:** «MongoDB, бо він швидкий і в ньому зручний JSON».

**Сильно:** «Спершу подивився б на дані й операції. Замовлення пов'язані з користувачами й товарами, потрібні транзакції на списання залишків і звіти по продажах – це аргументи за PostgreSQL. Якщо б окремим сервісом був, наприклад, кошик або стрічка переглядів, де дані читаються одним агрегатом і транзакцій між сутностями немає, там документна база або Redis доречніші. За замовчуванням почав би з PostgreSQL і виносив окремі профілі навантаження за потреби».

Різниця не в правильній назві, а в ході міркування: дані → операції → гарантії → вибір.

## Типові помилки у відповідях

1. **Бінарне мислення.** «SQL для серйозних систем, NoSQL для стартапів» – ярлики замість аналізу. Реальні системи комбінують: PostgreSQL як джерело правди, Redis для кешу, іноді документна база під окремий профіль.
2. **Ігнорування запитів.** Кандидат моделює дані, не спитавши, ЯК вони читатимуться. А саме запити визначають, чи буде модель зручною: документна база чудова рівно до першого «а тепер звіт у розрізі товарів».
3. **Денормалізація без плану оновлення.** Скопіював назву товару в документ замовлення – добре; не відповів, що станеться при перейменуванні – мінус. Кожна копія даних має власника і правило життя.
4. **«Транзакції не потрібні, у нас простий продукт».** Достатньо одного переказу коштів, бронювання чи ліміту місць, щоб вони стали потрібні. Краще показати, що ти бачиш такі місця заздалегідь.
5. **Індекси поза увагою.** Різниця між «працює» і «працює на реальних даних» – найчастіше саме індекси. Згадка про EXPLAIN і індекс під конкретний запит одразу піднімає рівень відповіді.

## Що потренувати перед співбесідою

1. Напиши схему з прикладу вище руками і три JOIN-запити до неї ([документація PostgreSQL](https://www.postgresql.org/docs/) – найкращий довідник).
2. Змоделюй ту саму область документами і сформулюй, які запити стали простішими, а які болючішими.
3. Підготуй по одному прикладу задачі «точно SQL» і «точно документи» зі своїм поясненням.

База даних – лише один шар backend-співбесіди; повний маршрут підготовки є в статті про [Node.js interview](/blog/nodejs-backend-interview), а про кешування поверх бази – у розборі [Redis](/blog/redis-koly-potriben-kesh). Якщо готуєшся системно, глянь і [план підготовки за 30 днів](/blog/pidhotovka-do-tekhnichnoi-spivbesidy).
