ERD на собеседовании системного аналитика
Содержание:
Зачем ERD знать СА
ERD — основной инструмент СА для проектирования модели данных. Без чёткого ERD ТЗ для разработчика становится двусмысленным: непонятно, как связаны сущности, какие связи обязательные, какие могут быть пустыми.
На собесе типичные задачи:
- Спроектировать ERD для бизнес-домена (заказ, маркетплейс, банк)
- Найти проблемы в чужом ERD
- Объяснить, когда нормализовать, когда денормализовать
Сущности и атрибуты
Сущность — объект бизнес-домена (User, Order, Product). В ERD — прямоугольник.
Атрибут — характеристика сущности (User.email, Order.amount). В ERD — внутри прямоугольника.
Первичный ключ (PK) — уникальный идентификатор записи. Подчёркивается или помечается ключиком.
Виды атрибутов:
- Простой — один scalar (email)
- Составной — несколько частей (full_name = first_name + last_name)
- Производный — вычисляется из других (age из birth_date)
- Множественный — несколько значений на одну сущность (хобби) — обычно выносится в отдельную таблицу
Связи и кардинальность
Связь — как сущности соотносятся.
1:1
Один к одному. Пример: User и UserProfile (если профиль строго один на юзера).
Реализуется через FK на одной из сторон.
1:N
Один ко многим. Пример: Customer и Orders.
FK на стороне «многих»: Order.customer_id → Customer.id.
N:N
Многие ко многим. Пример: Students и Courses.
Реализуется через промежуточную (associative) таблицу: StudentCourse(student_id, course_id).
Обязательность
Связь может быть обязательной с одной или обеих сторон:
- У каждого Order должен быть Customer (обязательно с обеих сторон)
- У Customer может не быть Orders (необязательно со стороны Customer)
В Crow's Foot:
- Одна линия — обязательно
- Кружок (или две линии) — zero or one
- «Ножки» крокодила — many
Пример: Customer ||━━━<{ Order читается как «у Customer обязательно ноль или больше Orders».
Нотации Чена и Crow's Foot
Чен (Chen)
- Сущности — прямоугольники
- Атрибуты — овалы (рисуются вокруг сущности)
- Связи — ромбы между сущностями
- Кардинальность — числа над линиями (1, N, M)
Используется в учебниках. На практике редко.
Crow's Foot (Information Engineering)
- Сущности — прямоугольники с атрибутами внутри
- Связи — линии с символами кардинальности на концах
Используется в большинстве инструментов (draw.io, dbdiagram.io, PlantUML). На собесе предполагается по умолчанию.
Нормализация
Процесс приведения схемы к форме без избыточности.
1NF
Атомарные значения. Нет повторяющихся групп.
Плохо: User.phones = ["+79991234567", "+79997654321"] Хорошо: отдельная таблица UserPhone.
2NF
1NF + все non-key атрибуты зависят от полного primary key (важно для составных PK).
Плохо: OrderItem(order_id, product_id, product_name) — product_name зависит только от product_id, не от полного ключа. Хорошо: OrderItem(order_id, product_id, quantity), Product(id, name).
3NF
2NF + нет транзитивных зависимостей (non-key атрибут не зависит от другого non-key).
Плохо: Order(id, customer_id, customer_email) — customer_email зависит от customer_id, не от order.id. Хорошо: Order(id, customer_id), Customer(id, email).
BCNF
Усиление 3NF. Каждая нетривиальная зависимость идёт от суперключа.
На практике 3NF и BCNF почти идентичны. Достаточно знать 1-3NF на собесе.
Когда денормализуют
Нормализация — для целостности данных. Денормализация — для скорости чтения. Конфликт.
Денормализуют, когда:
- Много JOIN на горячем пути
- Read-heavy сценарий
- Аналитические таблицы (DWH часто полностью денормализован — star schema)
Проектирование схемы под задачу
На собесе типичная задача: «Спроектируй БД для маркетплейса» или «для системы доставки».
Подход:
- Слушай сценарии: какие действия в системе? Кто их делает?
- Выпиши сущности: User, Product, Order, OrderItem, Address, Payment, Review
- Опиши связи: User имеет много Orders, Order содержит OrderItems, OrderItem ссылается на Product
- Кардинальность: 1:1, 1:N, N:N? Обязательно или нет?
- Атрибуты: какие колонки нужны? Какие типы?
- Edge cases: что если Product удалён, а Order на него есть? Что с историей?
Пример для маркетплейса (минимум):
- User(id, email, created_at)
- Product(id, name, price, seller_id → User)
- Order(id, buyer_id → User, status, total, created_at)
- OrderItem(order_id → Order, product_id → Product, quantity, price_at_purchase)
- Address(id, user_id → User, street, city, zip)
Заметьте price_at_purchase в OrderItem — это денормализация для сохранения цены на момент заказа (Product.price может потом измениться).
Частые ошибки
Игнорировать историчность. Если цена Product меняется, а в Order сохраняется только product_id — для исторического заказа нельзя восстановить цену. Используйте SCD или денормализацию.
Soft delete без подумать. Удалять Product физически — сломаются Orders, ссылающиеся на него. Альтернативы: soft delete (is_deleted), архивная таблица, запрет на удаление пока есть связанные данные.
Слишком сильная нормализация. «Каждое значение в отдельной таблице» = JOIN-кошмар. 3NF обычно достаточна.
Не указывать тип атрибута. На собесе попросят: «Какой тип у Order.created_at?» Если отвечаете «дата» — мало. Нужно TIMESTAMP WITH TIME ZONE или TIMESTAMPTZ.
Не подумать про индексы. На собесе СА индексы часто пропускают, но они влияют на дизайн. Кандидат, который пометил индексируемые колонки — плюс.
FAQ
dbdiagram.io vs draw.io — что выбрать?
dbdiagram.io лучше для ERD: текстовый DSL, автоматическая визуализация, легко поделиться. draw.io универсальнее, но рисуется руками.
Сколько ERD спроектировать перед собесом?
3-5 для разных доменов: маркетплейс, банк, доставка, образовательная платформа. Тренируйтесь по таймеру, 30 минут на ERD.
Это официальная информация?
Нет. Статья основана на классических работах Чена и опыте кандидатов.