ERD и связи 1:1 / 1:N / N:M на собесе системного аналитика
Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.
Содержание:
Зачем ERD спрашивают
ERD (Entity-Relationship Diagram) — основной инструмент описания структуры данных. На собесе SA дадут кейс: «спроектируйте схему БД для интернет-магазина» — ожидают ERD с сущностями, атрибутами, связями. Без понимания типов связей кандидат рисует кривые стрелки и теряет балл.
Главная боль без ERD — ТЗ из 30 страниц текста, в котором не понятно, может ли у пользователя быть несколько адресов. Разраб догадывается, делает один FK, через месяц обнаруживается, что у юриков 5 адресов. Переделка таблиц, миграции, ошибки.
ERD — обязательный артефакт ТЗ. На собесе спросят: «какие связи бывают?», «как реализуется N:M?», «чем 1:1 отличается от 1:N с UNIQUE?»
Сущности и атрибуты
Сущность (Entity) — объект предметной области: User, Order, Product, Address.
Атрибут (Attribute) — свойство сущности: name, email, created_at.
Виды атрибутов:
- Простой / составной —
phonevsaddress (street + city + zip). Составной обычно разбиваем на простые. - Однозначный / многозначный —
birth_datevsphone (несколько). Многозначные → отдельная сущность. - Хранимый / производный —
birth_date(хранимый) vsage(производный из birth_date) - Ключевой / неключевой — primary key, alternate key, surrogate key
Идентификатор:
- Natural key — атрибут предметной области (ИНН для юрлица, ISO-код для страны)
- Surrogate key — искусственный (auto-increment, UUID), не несёт бизнес-смысла
В современных схемах surrogate почти всегда. Natural — там, где гарантированно неизменен.
Связь 1:1
Один экземпляр сущности A связан с одним экземпляром B и наоборот.
Пример: User ↔ UserSettings. У каждого пользователя — одна запись настроек, одна запись настроек принадлежит одному пользователю.
CREATE TABLE users (
user_id BIGINT PRIMARY KEY,
email VARCHAR(255)
);
CREATE TABLE user_settings (
user_id BIGINT PRIMARY KEY REFERENCES users(user_id),
theme VARCHAR(20),
notifications BOOLEAN
);Реализация: FK + UNIQUE constraint на колонке (или PK = FK как в примере).
Когда 1:1 нужно:
- Разделить часто и редко используемые поля (для performance)
- Опциональные блоки атрибутов (subscription, profile)
- Изоляция чувствительных данных (PII в отдельной таблице с другими правами доступа)
В большинстве случаев 1:1 можно «слить в одну таблицу», но иногда оправданно разделять.
Связь 1:N
Один экземпляр A связан с N экземплярами B; каждый B связан с одним A.
Пример: User → Orders. У одного юзера много заказов, каждый заказ принадлежит одному юзеру.
CREATE TABLE users (user_id BIGINT PRIMARY KEY);
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
user_id BIGINT REFERENCES users(user_id),
amount DECIMAL,
created_at TIMESTAMP
);FK на «многой» стороне (orders.user_id) ссылается на PK «одной» стороны.
Кратность (cardinality): обычно описывают как (min, max):
(0, N)— у юзера может не быть ни одного заказа(1, N)— у юзера обязательно есть хотя бы один заказ
Cardinality описывается в ТЗ: «у каждого юзера обязательно один или больше адресов» → (1, N).
Связь N:M
Каждый A связан с N экземплярами B; каждый B связан с M экземплярами A.
Пример: Student ↔ Courses. Студент учится на нескольких курсах, на курсе — много студентов.
В реляционной модели N:M реализуется через junction-таблицу (associative entity):
CREATE TABLE students (student_id BIGINT PRIMARY KEY, name VARCHAR);
CREATE TABLE courses (course_id BIGINT PRIMARY KEY, title VARCHAR);
CREATE TABLE student_courses (
student_id BIGINT REFERENCES students(student_id),
course_id BIGINT REFERENCES courses(course_id),
enrolled_at TIMESTAMP DEFAULT now(),
grade VARCHAR(2),
PRIMARY KEY (student_id, course_id)
);Junction-таблица сама может иметь атрибуты — enrolled_at, grade. Это associative entity, может стать самостоятельной сущностью.
Грабля: N:M «массивом ID» (courses_ids = [1, 2, 3] в строке студента) — нарушение 1НФ. Junction-таблица — единственный правильный путь в реляционной БД.
В NoSQL (Mongo, Cassandra) embedded arrays приемлемы.
Crow's foot нотация
Самая распространённая нотация для ERD. Конец линии показывает кратность:
| Символ | Значение |
|---|---|
| `─ | |
| `─o | ` |
| `─ | <` |
─o< |
Ноль или больше (optional many) |
Пример: User ─||──o<─ Order читается как «один user может иметь ноль или больше orders, каждый order связан ровно с одним user».
Альтернативные нотации:
- Чен (Chen) — ромбы для связей, прямоугольники для сущностей. Академично, на практике редко.
- UML class diagram —
1,0..*,1..*числами на концах. В ИТ-документации норма. - IDEF1X — ГОСТ/госовские стандарты.
В РФ Crow's foot и UML — наиболее частые.
Частые ошибки
Не различать N:M и 1:N с массивом. «Студент учится на 5 курсах — поле courses_ids в students» — это N:M через массив, нарушение реляционной модели.
Опускать junction-таблицу. Без неё нельзя добавить атрибуты связи (когда поступил, какая оценка). Junction обязательна.
Optional vs mandatory не указано. «У юзера есть адрес» — у каждого или у некоторых? (0, N) или (1, N)? В ТЗ обязательно явно.
Surrogate key для junction. Часто PK junction-таблицы = составной (student_id, course_id). Surrogate (enrollment_id) — опционально, для удобства FK на эту запись из других таблиц.
Зацикленные FK. A ссылается на B, B ссылается на A. Создать одну из них без другой нельзя. Решения: nullable FK + двухшаговая вставка, или deferred constraints.
Не моделировать историю. «У юзера есть менеджер» = manager_id в users. Завтра менеджер сменился — историю потеряли. Если важна история — junction user_managers (user_id, manager_id, valid_from, valid_to).
ERD без cardinalities. На картинке стрелочки без 1/N/0..* — бесполезный артефакт, разраб додумывает.
Связанные темы
- Нормализация БД на собеседовании SA
- ACID и уровни изоляции на собесе SA
- Подготовка к собесу системного аналитика
- Сoбеседование системного аналитика
- SQL vs NoSQL: когда что
FAQ
Чем UML class diagram отличается от ERD?
ERD — про данные (сущности, атрибуты, связи). UML class — про объекты с поведением (методы) в OOP. Часто используются вместе: ERD на стадии моделирования данных, UML class на дизайн классов сервиса.
Можно ли N:M реализовать без junction-таблицы?
В реляционной БД — нет. В NoSQL — embedded array, но потеря целостности и атрибутов связи. Junction-таблица — стандарт.
Что такое слабая сущность?
Сущность, чей идентификатор зависит от другой сущности. Например, OrderItem идентифицируется парой (order_id, line_no) — без order_id смысла нет. На ERD рисуется двойным прямоугольником.
ERD до или после use cases?
Параллельно. Use cases дают функциональные требования, ERD — модель данных. Use case «оформить заказ» подсказывает сущности (Order, OrderItem, User, Product), ERD конкретизирует связи и атрибуты.
Когда добавлять surrogate key к junction?
Когда другие таблицы должны ссылаться на конкретное участие в связи (на «факт записи на курс» как сущность). Также удобно для логов и audit trail.
Это официальная информация?
Нет. Статья основана на работах Чена (1976), Codd, Crow's foot нотации (1981) и общей практике моделирования.
Тренируйте системный анализ — откройте тренажёр с 1500+ вопросами для собесов.