Реляционная БД, SQL и ORM — за один присест
Когда говорят «база данных» — в 99% случаев имеют в виду реляционную БД: Postgres, MySQL, SQLite, SQL Server. Все они работают по одной модели, придуманной в 1970-м: данные лежат в таблицах со строгими колонками, между таблицами есть связи через id-ключи, читаешь и пишешь языком SQL. Этой модели 50+ лет — и она до сих пор побеждает «модные» альтернативы (MongoDB, Firebase) в почти любом продукте сложнее заметок. Разберём почему.
Таблицы, строки, колонки — базовая ментальная модель
Думай о БД как об Excel с правилами. Каждая таблица — лист. Колонки заранее определены и типизированы (id — число, email — строка длиной 255, createdAt — дата). Каждая строка — одна запись (один юзер, один заказ). У каждой строки есть уникальный id (первичный ключ) — по нему её можно найти за миллисекунду даже среди миллионов. В отличие от Excel, БД железно следит за типами и ограничениями: попробуешь записать "abc" в числовую колонку — БД откажется. Эта строгость и делает БД надёжной.
SQL за 5 минут — четыре глагола, которые решают всё
SELECT (читай), INSERT (создавай), UPDATE (меняй), DELETE (удаляй). 90% запросов — это SELECT. WHERE фильтрует строки, ORDER BY сортирует, LIMIT ограничивает количество. JOIN склеивает таблицы по ключам. GROUP BY группирует и считает (сколько заказов у каждого юзера). SQL декларативный — ты говоришь ЧТО хочешь получить, движок сам решает КАК. Поэтому один и тот же запрос работает быстро на 10 строках и на 10 миллионах — БД оптимизирует план выполнения сама.
Связи: один-к-одному, один-ко-многим, многие-ко-многим
Юзер ↔ профиль (1:1) — один юзер имеет ровно один профиль, profile.userId уникален. Юзер → заказы (1:N) — один юзер делает много заказов, order.userId без unique. Студент ↔ курсы (N:M) — студент может ходить на несколько курсов, курс — на несколько студентов; нужна третья таблица enrollment с (studentId, courseId). Эти три варианта покрывают 95% реальных задач. Когда придумываешь схему — для каждой пары сущностей задавай вопрос: один-к-одному, один-ко-многим или многие-ко-многим?
Зачем ORM (и почему ты используешь Prisma, а не пишешь SQL руками)
ORM — Object-Relational Mapping — прослойка, которая превращает SQL-таблицы в обычные объекты твоего языка. Вместо
пишешь
Плюсы: типобезопасность (TypeScript знает структуру таблиц), защита от SQL-инъекций (параметры экранируются автоматически), миграции (изменения схемы версионируются), автокомплит. Минусы: иногда генерит неоптимальный SQL, и для сложной аналитики приходится писать сырой SQL через prisma.$queryRaw. Вывод: 95% задач делай через Prisma, для оставшихся 5% — учись читать SQL.
Реляционная vs NoSQL — когда что
NoSQL (MongoDB, Firebase, DynamoDB) — данные хранятся как JSON-документы без жёсткой схемы. Плюсы: гибкость на старте, легко складывать вложенные структуры. Минусы — сложно делать связи (нет JOIN), нет транзакций между документами, под нагрузкой данные дублируются и расходятся. Реляционная БД (Postgres) выигрывает когда у тебя: связи между сущностями (юзеры-заказы-товары), нужны транзакции (платежи, инвентарь), нужна строгая схема (финансы, медицина). NoSQL уместен только для логов, кэша или продуктов где документы реально независимые (заметки, файлы). Если сомневаешься — бери Postgres.
-- SQL: создать юзера и его заказ
INSERT INTO users (email, name) VALUES ('anna@example.com', 'Анна');
INSERT INTO orders (user_id, amount, status)
VALUES (1, 1500, 'pending');
-- SQL: достать всех юзеров с их заказами (JOIN)
SELECT u.name, COUNT(o.id) AS orders_count, SUM(o.amount) AS total
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name
ORDER BY total DESC;
// Prisma — то же самое, но типобезопасно
const anna = await prisma.user.create({
data: {
email: 'anna@example.com',
name: 'Анна',
orders: {
create: { amount: 1500, status: 'pending' },
},
},
});
// Юзеры с заказами — Prisma включает связь автоматически
const usersWithOrders = await prisma.user.findMany({
include: { orders: true },
orderBy: { createdAt: 'desc' },
});
// Сложная аналитика — иногда проще сырой SQL
const top = await prisma.$queryRaw`
SELECT u.name, COUNT(o.id) AS orders, SUM(o.amount) AS total
FROM users u LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name
ORDER BY total DESC LIMIT 10
`;SQL и Prisma — параллель