EvolCode
треки/Базы данных·01 / 07
15 мин чтения

Реляционная БД, 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-таблицы в обычные объекты твоего языка. Вместо

$ SELECT * FROM users WHERE email = ?

пишешь

$ prisma.user.findUnique({where: {email}})

Плюсы: типобезопасность (TypeScript знает структуру таблиц), защита от SQL-инъекций (параметры экранируются автоматически), миграции (изменения схемы версионируются), автокомплит. Минусы: иногда генерит неоптимальный SQL, и для сложной аналитики приходится писать сырой SQL через prisma.$queryRaw. Вывод: 95% задач делай через Prisma, для оставшихся 5% — учись читать SQL.

Реляционная vs NoSQL — когда что

NoSQL (MongoDB, Firebase, DynamoDB) — данные хранятся как JSON-документы без жёсткой схемы. Плюсы: гибкость на старте, легко складывать вложенные структуры. Минусы — сложно делать связи (нет JOIN), нет транзакций между документами, под нагрузкой данные дублируются и расходятся. Реляционная БД (Postgres) выигрывает когда у тебя: связи между сущностями (юзеры-заказы-товары), нужны транзакции (платежи, инвентарь), нужна строгая схема (финансы, медицина). NoSQL уместен только для логов, кэша или продуктов где документы реально независимые (заметки, файлы). Если сомневаешься — бери Postgres.

примерsql
-- 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 — параллель

главное
  • 01Реляционная БД = таблицы со строгими колонками + связи через id-ключи
  • 02SQL — 4 глагола: SELECT, INSERT, UPDATE, DELETE. 90% работы — SELECT с WHERE и JOIN
  • 03Три типа связей покрывают 95% задач: 1:1, 1:N, N:M (последняя через join-таблицу)
  • 04ORM (Prisma) превращает таблицы в типизированные объекты — пиши через неё, не сырой SQL
  • 05NoSQL хорош только для документов без связей. Для бизнес-логики со связями — Postgres
  • 06Декларативный SQL: говоришь ЧТО хочешь, БД сама решает КАК — поэтому один код работает на 10 строках и на 10 миллионах
  • 07Знать SQL хотя бы на чтение — обязательно. Сложная аналитика всё равно делается через $queryRaw
проверь себя
01У тебя есть юзеры и теги. Один юзер может иметь много тегов, один тег — у многих юзеров. Какой тип связи и сколько таблиц нужно?
02Зачем нужен ORM вроде Prisma, если можно писать SQL?
03Когда выбирать NoSQL (MongoDB) вместо Postgres?
04Что делает SELECT декларативным языком?
следующий урок: Как спроектировать схему БД, чтобы потом не плакать