Ваш дизайн нарушает Четвертая нормальная форма . Вы пытаетесь сохранить несколько «фактов» в одной таблице, и это приводит к аномалиям.
Таблица Person_Attributes должна выглядеть примерно так: personId jobId houseId restaurantId
Так что, если я связываюсь с одной работой, одним домом, но двумя ресторанами, храню ли я следующую?
personId jobId houseId restaurantId
1234 42 87 5678
1234 42 87 9876
А если я добавлю третий ресторан, я скопирую другие столбцы?
personId jobId houseId restaurantId
1234 123 87 5678
1234 123 87 9876
1234 42 87 13579
Готово! Ой, подожди, что там произошло? Я сменил работу одновременно с добавлением нового ресторана. Теперь я неправильно связан с двумя заданиями, но нет никакого способа различить это и правильно , связанное с двумя заданиями.
Кроме того, даже если правильно связать с двумя заданиями, не должны ли данные выглядеть так?
personId jobId houseId restaurantId
1234 123 87 5678
1234 123 87 9876
1234 123 87 13579
1234 42 87 5678
1234 42 87 9876
1234 42 87 13579
Он начинает выглядеть как декартово произведение всех различных значений jobId, houseId и restaurantId. На самом деле это так - потому что эта таблица пытается хранить несколько независимых фактов.
Правильный реляционный дизайн требует отдельной таблицы пересечений для каждого отношения «многие ко многим». Извините, вы не нашли ярлык.
(Многие статьи о нормализации говорят, что высшие нормальные формы после 3NF являются эзотерическими, и никогда не нужно беспокоиться о 4NF или 5NF. Пусть этот пример опровергает это утверждение.)
Ваш комментарий об использовании NULL: тогда у вас возникла проблема с обеспечением уникальности, поскольку ограничение PRIMARY KEY
требует, чтобы все столбцы были NOT NULL.
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL 9876
1234 NULL NULL 13579
Кроме того, если я добавлю второй дом или второй jobId в таблицу выше, в какую строку я добавлю это? Вы можете закончить с этим:
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL 9876
1234 42 NULL 13579
Теперь, если я отключу restaurantId 9876, я мог бы обновить его до NULL. Но это оставляет ряд всех NULL, которые я действительно должен просто удалить.
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL NULL
1234 42 NULL 13579
Принимая во внимание, что если бы у меня был отключенный ресторан 13579, я мог бы обновить его до NULL и оставить ряд на месте.
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL 9876
1234 42 NULL NULL
Но не следует ли мне объединить строки, перенеся jobId в другую строку, если в этом столбце есть вакансия?
personId jobId houseId restaurantId
1234 123 87 5678
1234 42 NULL 9876
Проблема в том, что теперь становится все сложнее добавлять или удалять ассоциации, требуя нескольких операторов SQL для внесения изменений. Вам придется написать много утомительного кода приложения, чтобы справиться с этой сложностью.
Тем не менее, все различные изменения просты, если вы определяете одну таблицу на отношение многие-ко-многим. Вам действительно нужна сложность иметь такое количество таблиц, но при этом вы упростите код приложения.
Добавление ассоциации в ресторан - это просто INSERT
в таблице Person_Restaurant. Удаление этой ассоциации просто DELETE
. Неважно, сколько существует ассоциаций с работой или домом. И вы можете определить ограничение первичного ключа в каждой из этих таблиц пересечений для обеспечения уникальности.