ERD и связи 1:1 / 1:N / N:M на собесе системного аналитика

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — 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.

Виды атрибутов:

  • Простой / составнойphone vs address (street + city + zip). Составной обычно разбиваем на простые.
  • Однозначный / многозначныйbirth_date vs phone (несколько). Многозначные → отдельная сущность.
  • Хранимый / производныйbirth_date (хранимый) vs age (производный из 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).

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Связь 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 diagram1, 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..* — бесполезный артефакт, разраб додумывает.

Связанные темы

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+ вопросами для собесов.