В таблице позиций заказа название товара определяется только номером товара, хотя ключ строки составной. Ка...

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

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

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

Это частичная функциональная зависимость: название товара зависит только от части составного ключа, а не от всей строки. Такая зависимость нарушает вторую нормальную форму (2НФ); обычно название выносят в таблицу товаров.

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

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

Разделение сущностей позволяет хранить факт один раз: свойства товара — в таблице товаров, а связь товара с заказом и количество — в таблице позиций заказа.

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

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

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

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

Для 2НФ требуется, чтобы каждый неключевой атрибут зависел от всего составного ключа, а не от его части. В данном случае количество зависит от пары заказ–товар, а название определяется только товаром: это и есть частичная зависимость.

Типовая декомпозиция разделяет текущие данные товара и данные позиции заказа:

CREATE TABLE products ( product_id bigint PRIMARY KEY, product_name text NOT NULL ); CREATE TABLE order_items ( order_id bigint NOT NULL, product_id bigint NOT NULL, quantity integer NOT NULL, PRIMARY KEY (order_id, product_id), FOREIGN KEY (product_id) REFERENCES products(product_id) );

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

Декомпозиция должна быть без потери информации: соединение позиций с товарами по product_id должно восстанавливать нужные сведения. Нормализация не означает, что любое повторение запрещено: исторический снимок названия или цены в заказе может быть самостоятельным бизнес-атрибутом.

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

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

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

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

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

  1. Всегда ли наличие составного ключа означает нарушение 2НФ?

Нет. Нарушение возникает только при наличии неключевого атрибута, зависящего от proper-подмножества составного ключа. Если все неключевые атрибуты зависят от всей комбинации, 2НФ не нарушена.

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

  1. Почему разбиение на таблицы не должно менять набор фактов?

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

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

  1. Нужно ли удалять название товара из позиции заказа, если его дублирование нарушает 2НФ?

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

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