Программирование SQLПроектирование схемы и ограниченияПроектировщик реляционных баз данных

После разбиения отношения на несколько таблиц данные восстанавливаются без ложных строк, но одну исходную ф...

После разбиения отношения на несколько таблиц данные восстанавливаются без ложных строк, но одну исходную функциональную зависимость нельзя проверить без соединения. Какое свойство декомпозиции утрачено?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

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

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

Исторический контекст

Нормализация появилась как способ уменьшить избыточность данных и предотвратить аномалии вставки, изменения и удаления. При разбиении исходного отношения проектировщик стремится одновременно сохранить данные, зависимости и возможность корректно восстанавливать исходное отношение.

На практике эти цели могут конфликтовать. Например, более строгая нормализация иногда приводит к тому, что бизнес-правило больше нельзя выразить одним ограничением в одной таблице.

Постановка проблемы

Пусть в исходном отношении действуют зависимости A → B и B → C. Если его разделить на отношения A–B и A–C, зависимость A → B можно проверять в первой таблице, но зависимость B → C больше не представлена целиком ни в одной таблице.

Чтобы проверить последнее правило, потребуется соединить таблицы и убедиться, что для каждого значения B всегда используется одно значение C. Простые ограничения первичного ключа, уникальности и внешнего ключа сами по себе такую проверку не обеспечивают.

Подробное решение

Сохранение зависимостей означает, что ограничения исходной схемы можно заменить ограничениями на декомпозированных отношениях так, чтобы совокупность этих ограничений имела ту же силу для исходных зависимостей. Проверка не должна требовать восстановления исходного отношения через соединение.

Например, после неудачной декомпозиции можно получить такую структуру:

CREATE TABLE employee_department ( employee_id BIGINT PRIMARY KEY, department_id BIGINT NOT NULL ); CREATE TABLE employee_head ( employee_id BIGINT PRIMARY KEY, head_id BIGINT NOT NULL );

Если исходное правило было department_id → head_id, эта схема не гарантирует его. В ней два сотрудника одного отдела могут ссылаться на разных руководителей, поскольку столбцы department_id и head_id находятся в разных строках и таблицах; внешний ключ проверяет существование записи, но не функциональную зависимость между ними.

Для сохранения зависимости нужна таблица, в которой ее детерминант и зависимый атрибут находятся вместе:

CREATE TABLE department_head ( department_id BIGINT PRIMARY KEY, head_id BIGINT NOT NULL );

Теперь первичный ключ department_id непосредственно гарантирует, что у отдела не более одного руководителя. Таблица сотрудников может ссылаться на department_head внешним ключом, а правило проверяется без соединения всех сотрудников.

Свойство без потерь решает другую задачу: обратное соединение таблиц не должно порождать ложные строки и должно восстанавливать исходные данные. Поэтому при проектировании проверяют оба свойства отдельно. Декомпозиция без потерь, но без сохранения зависимостей может быть теоретически корректной по данным, однако неудобной или небезопасной для обеспечения целостности.

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

Ситуация из практики

В системе организации сотрудников сначала хранили сотрудника, отдел и руководителя в одной таблице. Затем таблицу разделили на связи сотрудник–отдел и сотрудник–руководитель, чтобы убрать повторение имени руководителя.

Рассматривались два варианта. Прикладная проверка была простой в реализации, но могла пропустить конфликт при параллельных транзакциях. Триггер позволял централизовать правило, однако усложнял сопровождение и требовал аккуратной настройки блокировок. Возврат связи отдел–руководитель в отдельную таблицу сохранял нормализацию и позволял выразить правило первичным ключом.

Выбрали таблицу отдел–руководитель, а в таблице сотрудников оставили только идентификатор отдела и внешний ключ. В результате руководитель задаётся ровно один раз, изменение сразу видно всем сотрудникам, а проверка целостности выполняется декларативными ограничениями.

Что кандидаты часто упускают

  1. Можно ли считать зависимость сохранённой, если её удаётся проверить запросом с соединением?

Нет. Это означает, что зависимость выявляема, но не обязательно сохранена в смысле проектирования схемы. Сохранение зависимостей требует возможности обеспечить правило ограничениями отдельных декомпозированных отношений без постоянного восстановления исходного отношения.

Запрос или периодическая проверка может обнаружить уже существующее нарушение, но не гарантирует, что между двумя проверками некорректная транзакция не запишет конфликтующие данные.

  1. Почему внешний ключ обычно не заменяет сохранение функциональной зависимости?

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

Например, внешний ключ employee_head(department_id) может гарантировать существование отдела, но не запретит двум строкам с одним отделом указывать на разных руководителей. Для этого department_id должен быть уникальным в таблице, где хранится соответствие отдел–руководитель.

  1. Всегда ли нужно жертвовать нормализацией ради сохранения зависимостей?

Нет. Сначала следует поискать другую декомпозицию, которая одновременно является декомпозицией без потерь и сохраняет зависимости. Часто синтез схемы в третьей нормальной форме как раз позволяет разместить каждую функциональную зависимость в отдельной таблице.

Денормализация или триггеры оправданы, когда нужную зависимость невозможно удобно выразить схемой либо требования производительности и интеграции перевешивают усложнение. Такое решение требует явно определить транзакционные границы, конкурентный доступ и способ восстановления целостности после сбоев.