Дисциплина: ОП.08 Основы проектирования баз данных
Тема рабочей программы (§2.2 с.7): Тема 2. Взаимосвязи в моделях и реляционный подход к построению моделей — Практическое занятие №4: проектирование реляционной БД
Учебный модуль: Третья нормальная форма (3NF), форма Бойса-Кодда (BCNF), DDL-спецификация и валидация тестовых данных
Проектирование реляционной базы данных — это строгий инженерный процесс трансформации неформальных требований заказчика в математически корректную и оптимизированную физическую модель хранения данных. Этот процесс включает четыре последовательных уровня:
INSERT INTO) и проведение негативного тестирования ограничений.Фундаментом реляционной нормализации является теория функциональных зависимостей (ФЗ). Запись X → Y означает, что значение набора атрибутов X однозначно определяет значение набора атрибутов Y в рамках любого допустимого состояния отношения. Набор X называется детерминантом.
Вспомним критерии нормальных форм:
Пусть в таблице сотрудников задано отношение со схемой: Сотрудники(Таб_номер, ФИО, Должность, Базовый_оклад).
Здесь Таб_номер — первичный ключ. В организации действует штатное расписание: базовый оклад жестко зафиксирован за должностью. Имеем функциональные зависимости:
Таб_номер → Должность (первичный ключ определяет должность сотрудника);Должность → Базовый_оклад (должность определяет установленный оклад);Должность не является потенциальным ключом (несколько людей могут занимать одну должность), а Базовый_оклад не входит в ключ.Следовательно, зависимость Таб_номер → Базовый_оклад является транзитивной ($X \to Y \to Z$). Это приводит к четырем классическим аномалиям:
| Вид аномалии | Как проявляется в ненормализованной таблице | Инженерное последствие |
|---|---|---|
| Аномалия избыточности | Оклад должности «Инженер» (75 000 руб.) повторяется в каждой из 300 строк инженеров. | Неоправданный расход дисковой памяти, раздувание B-Tree индексов, лишний ввод данных оператором. |
| Аномалия обновления (Update Anomaly) | При индексации оклада инженеров до 85 000 руб. требуется обновить все 300 строк. | При аппаратном сбое или прерывании запроса часть сотрудников получит новый оклад, а часть останется со старым — база перейдет в рассогласованное состояние. |
| Аномалия вставки (Insertion Anomaly) | Руководство ввело новую должность «Архитектор БД» со ставкой 150 000 руб., но вакансия еще открыта. | Невозможно внести должность в базу, так как поле Таб_номер (Primary Key) не может принимать значение NULL (нарушение целостности сущности). |
| Аномалия удаления (Deletion Anomaly) | Уволился единственный сотрудник в должности «Специалист по информационной безопасности». | При удалении строки сотрудника из базы навсегда стирается сама должность и утвержденный для нее размер оклада. |
Выпишите все атрибуты отношения и выявите все смысловые зависимости бизнес-логики. Определите первичный ключ $X$.
Найдите любую зависимость $Y \to Z$, где левая часть $Y$ не содержит первичного ключа, а правая часть $Z$ не входит в состав ключей. Это признак транзитивной зависимости $X \to Y \to Z$.
Разделите исходное отношение $R(X, A_1, \dots, Y, Z)$ на два независимых отношения:
FOREIGN KEY (Y) REFERENCES R_ref(Y)).Согласно теореме Хита (Heath's Theorem), разбиение отношения $R$ на $R_1$ и $R_2$ является обратимым без потерь тогда и только тогда, когда их пересечение $R_1 \cap R_2$ функционально определяет либо $R_1$, либо $R_2$. Поскольку $R_1 \cap R_2 = Y$, а $Y$ является первичным ключом для $R_{ref}$, естественное соединение гарантированно восстанавливает исходные данные без появления ложных строк.
Нормальная форма Бойса-Кодда (BCNF, Boyce-Codd Normal Form) была предложена Рэймондом Бойсом и Эдгаром Коддом в 1974 году как строгое усиление 3NF для отношений с множественными составными перекрывающимися ключами.
Сравнение с 3NF: В определении 3NF допускалось второе условие: «или $A$ — первичный атрибут». В BCNF это условие аннулировано. Любая ФЗ обязана исходить строго из суперключа. Отсюда следует: BCNF ⊂ 3NF ⊂ 2NF ⊂ 1NF (любое отношение в BCNF всегда находится в 3NF, но отношение в 3NF может нарушать BCNF).
Рассмотрим систему распределения учебных дисциплин на кафедре со следующими бизнес-правилами:
Академический_план(Студент, Предмет, Преподаватель).(Студент, Предмет) → Преподаватель.Преподаватель → Предмет.1. Потенциальные ключи отношения: {Студент, Предмет} и {Студент, Преподаватель}. Оба ключа являются составными и перекрываются по атрибуту Студент.
2. Проверка 3NF: Все атрибуты (Студент, Предмет, Преподаватель) входят в состав хотя бы одного потенциального ключа (являются первичными атрибутами). Зависимость Преподаватель → Предмет удовлетворяет условию 3NF, так как Предмет — первичный атрибут. Отношение находится в 3NF!
3. Проверка BCNF: В зависимости Преподаватель → Предмет детерминантом является Преподаватель. Является ли он суперключом? Нет! Зная только преподавателя, мы не можем однозначно определить студента. BCNF нарушена!
4. Аномалии из-за нарушения BCNF: Мы не можем зафиксировать в базе факт того, что новый преподаватель Иванов ведет предмет «Базы данных», пока к нему не записан хотя бы один студент (Insertion Anomaly).
Декомпозиция в BCNF: Отношение разбивается на две таблицы: Преподавание(Преподаватель, Предмет) (где Преподаватель — PK) и Назначения(Студент, Преподаватель) (составной PK). В обеих таблицах BCNF достигнута!
(Студент, Предмет) → Преподаватель оказывается межтабличной. Если студент запишется на курс к двум разным преподавателям по одному предмету, обычный FOREIGN KEY этого не предотвратит — потребуется триггер проверки. В практическом проектировании иногда осознанно останавливаются на 3NF, чтобы сохранить проверку всех ФЗ на уровне простых ограничений таблиц.
При выполнении SQL DDL-скрипта критически важен порядок операторов. Таблицы образуют ориентированный ациклический граф (DAG) зависимостей:
departments, manufacturers, courses, clients.positions, medicines, study_groups, vehicles.employees, batches, students, repair_orders.appointment_orders, sales_invoices, payments, work_items.Каждое поле реляционной таблицы должно быть жестко типизировано с учетом занимаемой памяти и точности вычислений:
INT / BIGINT: идентификаторы (ID), счетчики, внешние ключи.VARCHAR(N): строковые поля с жестким ограничением максимальной длины (не использовать неограниченный TEXT для коротких полей).NUMERIC(precision, scale): денежные суммы, цены, коэффициенты, физические дозировки. Категорически запрещено использовать типы с плавающей точкой (FLOAT / REAL) для финансовых величин из-за ошибок округления стандарта IEEE 754.DATE / TIMESTAMP: календарные даты и точные временные метки событий.BOOLEAN: логические флаги активности/архивности.| Ограничение | Назначение в архитектуре БД | Пример ANSI SQL синтаксиса |
|---|---|---|
PRIMARY KEY |
Обеспечивает целостность сущности, однозначную адресацию строки, не допускает NULL и дубликатов. | employee_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY |
FOREIGN KEY ... REFERENCES |
Обеспечивает ссылочную целостность, связывает дочернюю строку с существующей родительской. | position_id INT NOT NULL REFERENCES positions(position_id) ON DELETE RESTRICT |
UNIQUE |
Гарантирует неповторяемость естественных бизнес-ключей (ИНН, VIN, email, СНИЛС). | CONSTRAINT uq_client_phone UNIQUE (phone_number) |
NOT NULL |
Запрещает отсутствие значения в обязательных информационных полях. | full_name VARCHAR(150) NOT NULL |
CHECK |
Ограничивает домен значений предикатом бизнес-логики (неотрицательность, интервалы, перечисления). | CHECK (base_salary >= 19242.00)CHECK (expiry_date > manufacture_date) |
DEFAULT |
Задает системное значение поля при его отсутствии в операторе INSERT. | status VARCHAR(20) NOT NULL DEFAULT 'active' |
Опция ON DELETE во внешнем ключе определяет реакцию ядра СУБД на попытку удаления родительской записи:
ON DELETE RESTRICT (или NO ACTION): Запрещает удаление родительской строки, если на нее ссылается хотя бы одна дочерняя строка. СУБД возвращает ошибку foreign key violation и откатывает транзакцию. Это базовый выбор для всех нормативно-справочных таблиц (должности, отделы, клиенты, категории).ON DELETE CASCADE: Автоматически удаляет все связанные дочерние строки вслед за родителем. Допустимо исключительно для зависимых таблиц-спецификаций вида «мастер — детали» (например, при удалении черновика заказ-наряда удаляются строки выполненных в нем работ).ON DELETE SET NULL: Обнуляет поле внешнего ключа в дочерних строках. Требует, чтобы FK допускал значение NULL (не применимо для первичных компонентов связей M:N).Проектировщик базы данных обязан доказать работоспособность схемы на репрезентативном наборе данных. Набор операторов INSERT INTO должен удовлетворять трем критериям:
Строки вставляются строго в том порядке, в котором создавались таблицы: сначала родительские справочники, затем зависимые сущности. Нарушение порядка приведет к мгновенной ошибке ссылочной целостности:
-- 1. Сначала вставляем подразделения и должности
INSERT INTO departments (dept_id, dept_name) VALUES (1, 'Бухгалтерия');
INSERT INTO positions (pos_id, title, base_salary) VALUES (101, 'Главный бухгалтер', 95000.00);
-- 2. Только после этого вставляем сотрудника, ссылающегося на них
INSERT INTO employees (emp_id, full_name, dept_id, pos_id)
VALUES (1001, 'Смирнова Анна Викторовна', 1, 101);
-- ДЕМОНСТРАЦИЯ ЗАЩИТЫ: Попытка вставки отрицательного оклада (должна вызвать ошибку CHECK):
-- INSERT INTO positions (pos_id, title, base_salary) VALUES (102, 'Стажер', -500.00);
-- Ожидаемый результат СУБД: ERROR: new row for relation "positions" violates check constraint "chk_salary_positive"
NUMERIC(p, s) или DECIMAL(p, s).ON DELETE RESTRICT.UNIQUE(vin) оператор сможет 10 раз ввести один и тот же автомобиль с разными id, создав дубликаты и разрушив учет на СТО.