Я встраиваю компонент выставления счетов в свое веб-приложение.
В моей схеме есть уровни, на которых определенные клиенты могут захотеть выставить счет.
Projects -> Sites -> Jobs -> Phases -> Teams
Каждая из них является таблицей в базе данных и имеет отношение к своему родителю.
Я планирую построить Предметы таблица, которая будет содержать общую информацию о том, как мы будем выставлять счета за работу, выполняемую на каждом из этих уровней, то есть имя, цена по умолчанию, единица измерения и т. д.
Затем пользователь создаст Модель выставления счетов что они могут связать список Предметы чтобы, так что они могут быть многоразовыми.
Здесь мои вопросы вступают в игру. Когда пришло время выставить счет, мне нужно сообщить Модель выставления счетов на один из вышеупомянутых уровней. Я не хочу создавать таблицу связывания для каждого из этих уровней с моделью фактурирования. Я хотел бы как-то сохранить целостность отношений между таблицами. Но также, если есть что-то еще, что я могу выставить в будущем, я не хочу вносить существенные изменения в базу данных и / или изменить код, чтобы учесть этот новый «оплачиваемый уровень».
Могу ли я сохранить целостность отношений, не создавая новую таблицу для связи моделей выставления счетов с уровнем?
Могут быть реализованы полиморфные отношения, которые решают проблему присоединения «n» числа различных типов объектов.
Недостатком полиморфных отношений, насколько я знаю, является невозможность реализации без некоторой потери ссылочной целостности.
Хорошее чтение на этом будет https://hashrocket.com/blog/posts/modeling-polymorphic-associations-in-a-relational-database. Одной из самых популярных и простых реализаций будет Полиморфное соединение, цитируемое из статьи ниже:
Полиморфные соединения
Простой подход для подключения ACL к ресурсу состоит в использовании двух
столбцы в таблице acl: resource_type и resource_id. Этот подход
был популяризирован Ruby on Rails. Таблица ACL может быть определена как
следующим образом:
create table acl(
id serial primary key,
resource_type varchar not null,
resource_id integer not null,
-- other fields omitted
unique(resource_id, resource_type)
);
Запрос для получения acl для идентификатора документа: 42 был бы похож на
следующий:
select *
from acl
where resource_type='document'
and resource_id=42;
Серьезная проблема с этим подходом — база данных очень ограничена.
в целостности данных это может обеспечить из-за отсутствия внешнего ключа
ограничения. Это может гарантировать, что ресурс имеет не более одного acl, но
это все. Ресурсу может не хватать acl, а acl может указывать на
недостающий ресурс.
Метод Exclusive Belongs To — это хороший метод повышения ссылочной целостности, но для каждого возможного типа объекта потребуется новый столбец. Цитируется из статьи:
Эксклюзив Принадлежит (AKA Exclusive Arc) В этой модели у acl есть внешние ключи ко всем таблицам, к которым он может принадлежать.
create table acl(
id serial primary key,
document_id integer references document,
image_id integer references image,
file_id integer references file,
report_id integer references report,
-- other fields omitted
check(
(
(document_id is not null)::integer +
(image_id is not null)::integer +
(file_id is not null)::integer +
(report_id is not null)::integer
) = 1
)
);
create unique index on acl (document_id) where document_id is not null;
create unique index on acl (image_id) where image_id is not null;
create unique index on acl (file_id) where file_id is not null;
create unique index on acl (report_id) where report_id is not null;
Обратите внимание на ограничение проверки. Это гарантирует, что ACL принадлежит
ровно один ресурс любого типа. С этим дизайном ACL не может быть
осиротевший, но нет способа принудительно установить, что ресурс имеет ACL.
Также важны частичные уникальные индексы. Ограничение уникального
индексы только к ненулевым значениям значительно экономят место, а также
сокращение операций записи при вставке.
Чтобы четко ответить на ваш вопрос, вы не можете сохранить реляционную целостность, не создав по крайней мере еще две таблицы, одну для хранения реляционных идентификаторов между вашей Моделью и вашими Элементами и другую для хранения всех данных для Элементов, которые вы хотите создать, в этом Если у вас останется еще две таблицы, но боль в будущем будет излечена сейчас.