Теоретический конспект: Сущности и связи в реляционных базах данных

Дисциплина: ОП.08 Основы проектирования баз данных (СПО 09.02.07).
Тема: Тема 2. Взаимосвязи в моделях и реляционный подход к построению моделей — Практическое занятие №3: сущности и связи (§2.2 с.7 программы).
Фундаментальное руководство по трансляции концептуальных ER-моделей в реляционные структуры таблиц, декомпозиции связей многие-ко-многим и настройке ссылочной целостности.

1. Концептуальная и реляционная парадигмы: от диаграмм к таблицам

Проектирование базы данных состоит из последовательных уровней абстракции: концептуального (модель «сущность-связь», ERD), логического (реляционная схема отношений) и физического (конкретные таблицы в СУБД с индексами и типами хранения).

Определения:
  • Сущность (Entity) — концептуальный объект или понятие предметной области, информацию о котором необходимо хранить (например, Студент, Преподаватель, Товар).
  • Связь (Relationship) — семантическая ассоциация между двумя или более сущностями, отражающая бизнес-процессы реального мира (например, Студент сдает Экзамен, Покупатель оформляет Заказ).
  • Реляционная таблица (Relation / Table) — двумерный массив атомарных скалярных значений, состоящий из именованных столбцов (атрибутов) и строк (кортежей).

Сложность перехода заключается в том, что в реляционной теории Эдгара Кодда отсутствуют прямые физические конструкции для представления связей. Все связи моделируются исключительно совпадением значений первичных (PRIMARY KEY) и внешних (FOREIGN KEY) ключей.

2. Пошаговый алгоритм трансляции ER-модели в реляционную структуру

Трансляция концептуальной схемы выполняется строго по пятишаговому алгоритму, гарантирующему нормализацию и целостность:

┌─────────────────────────────────────────────────────────────┐ │ ШАГ 1. Трансляция сильных сущностей │ │ Каждая независимая сущность -> отдельная таблица. │ │ Выделение суррогатного PK (IDENTITY / SERIAL) и UNIQUE. │ └──────────────────────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ШАГ 2. Трансляция связей 1:1 │ │ Определение подчиненной таблицы. │ │ Размещение FK с ограничением UNIQUE NOT NULL или PK=FK. │ └──────────────────────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ШАГ 3. Трансляция связей 1:M │ │ Первичный ключ родителя (сторона 1) мигрирует │ │ как внешний ключ (FK) в таблицу на стороне «Многие» (M). │ └──────────────────────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ШАГ 4. Декомпозиция связей M:N │ │ Создание промежуточной ассоциативной таблицы (Junction). │ │ Добавление 2-х внешних ключей + атрибутов взаимодействия. │ │ Формирование составного PK: (fk_1, fk_2 [, role]). │ └──────────────────────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ШАГ 5. Настройка ссылочной целостности │ │ Назначение политик ON DELETE (RESTRICT / CASCADE / SET NULL)│ │ Проверка совпадения физических типов данных PK и FK. │ └─────────────────────────────────────────────────────────────┘

Детализация шагов трансляции:

  1. Шаг 1: Сильные сущности. Создаются независимые таблицы каталогов и справочников. Назначается первичный ключ INT или BIGINT. Для естественных кодов накладываются ограничения уникальности UNIQUE NOT NULL.
  2. Шаг 2: Связи 1:1. Выбирается ведущая и подчиненная сторона. В подчиненную таблицу добавляется колонка FOREIGN KEY UNIQUE. Если сущности разделяют одинаковый жизненный цикл (Покупатель — Корзина), первичный ключ корзины одновременно объявляется внешним ключом к покупателю.
  3. Шаг 3: Связи 1:M. Внешний ключ помещается строго в таблицу на стороне «многие». Например, у одного режиссера много фильмов — внешний ключ id_rezhissera добавляется в таблицу filmy.
  4. Шаг 4: Связи M:N. Прямая связь физически запрещена правилом атомарности (1NF). Создается новая связующая таблица. В нее мигрируют первичные ключи обеих связанных сущностей. Туда же переносятся все атрибуты связи.
  5. Шаг 5: Ссылочная целостность. Проверяется соответствие типов (например, INT ссылается строго на INT). Для справочников устанавливается ON DELETE RESTRICT, для зависимых строк спецификаций — ON DELETE CASCADE.

3. Архитектура связующих таблиц (Junction Tables)

Рассмотрим детальные промышленные примеры реализации ассоциативных таблиц для различных предметных областей.

Пример 1: Онлайн-кинотеатр (Связь Актеры ↔ Фильмы)

Связь многие-ко-многим: актер снимается во множестве фильмов, в фильме занято множество актеров. Атрибуты связи: роль (персонаж), признак главной роли, индивидуальный гонорар.

-- Таблица сильной сущности «Фильмы»
CREATE TABLE filmy (
    id_filma        INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    nazvanie        VARCHAR(150) NOT NULL,
    god_vypuska     SMALLINT NOT NULL CHECK (god_vypuska >= 1895),
    id_rezhissera   INT NOT NULL REFERENCES rezhissery(id_rezhissera) 
                        ON UPDATE CASCADE ON DELETE RESTRICT
);

-- Таблица сильной сущности «Актеры»
CREATE TABLE aktery (
    id_aktera       INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    familiya        VARCHAR(60) NOT NULL,
    imya            VARCHAR(60) NOT NULL,
    data_rozhdeniya DATE NOT NULL
);

-- Промежуточная связующая таблица «Фильм_Актер»
CREATE TABLE film_akter (
    id_filma        INT NOT NULL REFERENCES filmy(id_filma)
                        ON UPDATE CASCADE ON DELETE CASCADE,
    id_aktera       INT NOT NULL REFERENCES aktery(id_aktera)
                        ON UPDATE CASCADE ON DELETE RESTRICT,
    rol_personazh   VARCHAR(120) NOT NULL,
    is_glavnaya     BOOLEAN NOT NULL DEFAULT FALSE,
    gonorar         NUMERIC(12,2) CHECK (gonorar >= 0),
    PRIMARY KEY (id_filma, id_aktera, rol_personazh)
);
Обоснование составного PK: В ключ включено поле rol_personazh. Это позволяет одному и тому же актеру исполнять несколько ролей в одном фильме (например, близнецов или эпизодических персонажей).

Пример 2: Доставка еды (Связь Заказ ↔ Блюда меню)

Один заказ включает несколько блюд, блюдо входит во множество заказов. Атрибуты связи: количество порций, цена на момент заказа.

CREATE TABLE poziciya_zakaza (
    id_zakaza              BIGINT NOT NULL REFERENCES zakazy(id_zakaza)
                               ON UPDATE CASCADE ON DELETE CASCADE,
    id_blyuda              INT NOT NULL REFERENCES blyuda(id_blyuda)
                               ON UPDATE CASCADE ON DELETE RESTRICT,
    kolichestvo            SMALLINT NOT NULL DEFAULT 1 CHECK (kolichestvo > 0),
    cena_na_moment_zakaza  NUMERIC(8,2) NOT NULL CHECK (cena_na_moment_zakaza > 0),
    PRIMARY KEY (id_zakaza, id_blyuda)
);
Темпоральная целостность цены: Атрибут cena_na_moment_zakaza фиксирует историческую стоимость транзакции. Если цена в меню blyuda изменится, закрытый заказ сохранит исходную стоимость.

4. Обеспечение ссылочной целостности: RESTRICT, CASCADE, SET NULL

Внешний ключ (FOREIGN KEY) гарантирует, что значение в дочерней таблице обязано соответствовать существующему первичному ключу в родительской таблице, либо быть равным NULL.

Опция действия Поведение при DELETE родительской строки Типичные кейсы применения
RESTRICT / NO ACTION Транзакция прерывается с ошибкой, если на родительскую строку ссылается хотя бы одна дочерняя запись. Справочники сотрудников, авторов, категорий товаров, аккаунты клиентов с историей заказов.
CASCADE СУБД автоматически физически удаляет все зависимые дочерние записи. Позиции в удаляемом заказе, строки корзины покупок, связующие записи кастинга при удалении фильма.
SET NULL СУБД присваивает внешнему ключу значение NULL. Дочерняя колонка не должна иметь NOT NULL. Необязательные ссылки: промокод на скидку, куратор студенческой группы, менеджер сделки.

5. Вопросы для глубокой самопроверки

  1. Почему реляционные базы данных физически не поддерживают связь многие-ко-многим (M:N) без промежуточной таблицы? Какое правило нормализации нарушается при попытке записать массив идентификаторов в строку?
  2. В какую именно таблицу (на стороне «один» или на стороне «многие») должен мигрировать внешний ключ при трансляции связи 1:M? Что произойдет, если поместить внешний ключ в обратную сторону?
  3. В каких ситуациях первичный ключ промежуточной связующей таблицы должен состоять из трех столбцов вместо двух? Приведите практический пример.
  4. Почему хранение цены покупки исключительно в таблице номенклатуры товаров является грубейшей архитектурной ошибкой проектирования баз данных?
  5. В чем заключается опасность установки политики ON DELETE CASCADE на внешний ключ связи «Заказ -> Клиент»? К каким последствиям для финансовой отчетности это приведет?
  6. Каким образом на уровне схемы таблиц физически гарантируется кардинальность связи 1:1 между таблицей «Покупатели» и таблицей «Корзины покупок»?