Как мне построить отношения между моими таблицами?

Я встраиваю компонент выставления счетов в свое веб-приложение. В My Schema у нас есть уровни, на которых определенные клиенты могут захотеть выставлять счета.

Projects -> Sites -> Jobs -> Phases -> Teams

Каждая из них является таблицей в базе данных и имеет отношение к своему родителю.

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

Затем пользователь создаст Модель выставления счетов, с которой он может связать список Элементов, чтобы их можно было использовать повторно.

Вот где мои вопросы вступают в игру. Когда пришло время выставлять счета, мне нужно связать Модель выставления счетов с одним из вышеупомянутых уровней. Я не хочу создавать таблицу привязки каждого из этих уровней к модели выставления счетов. Я хотел бы как-то сохранить реляционную целостность между таблицами. Но также, если есть что-то еще, за что в будущем я могу выставить счет, я не хочу вносить огромные изменения в базу данных и/или код, чтобы учесть этот новый «оплачиваемый уровень».

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


person Billy Stalnaker    schedule 17.07.2018    source источник
comment
Ссылочная целостность теряется при полиморфных отношениях, но похоже, что это может быть хорошим вариантом для вас.   -  person Devon    schedule 17.07.2018
comment
Я не совсем уверен, что понимаю, что мне нужно связать модель выставления счетов с одним из вышеупомянутых уровней. Если счет-фактура, как вы даже говорите, представляет собой список товаров, разве это не является взаимосвязью? У вас есть таблица Invoices, таблица Items и таблица Invoice_Item (invoiceId, itemId). У вас есть только одна таблица ссылок, а не по одной для каждого типа элемента.   -  person Patrick Q    schedule 17.07.2018
comment
@PatrickQ Модель выставления счетов — это, по сути, то, как кто-то выставляет счета, и я забыл упомянуть, что вы можете дважды выставить счет на один и тот же уровень для одной и той же модели выставления счетов. В конце будет таблица invoices и invoice_items. Но это происходит после того, как вы перейдете к выставлению счета за уровень.   -  person Billy Stalnaker    schedule 17.07.2018
comment
У вас может быть несколько обнуляемых FK для таблиц разных уровней в одной таблице, поэтому целостность будет там   -  person Iłya Bursov    schedule 17.07.2018
comment
@IlyaBursov Я тоже думал об этом решении. Но раньше это укусило меня за задницу, когда я пытался присоединиться к одной и той же таблице несколько раз, чтобы получить информацию для каждого уровня ниже рассматриваемого уровня.   -  person Billy Stalnaker    schedule 17.07.2018
comment
@ Devon Плюсы и минусы полиморфных отношений?   -  person Billy Stalnaker    schedule 17.07.2018
comment
@BillyStalnaker, плюсом будет возможность присоединять новые типы сущностей без необходимости расширять свои таблицы. Мошенничеством будет потеря некоторой ссылочной целостности в зависимости от реализации. Это может быть полезно прочитать: hashrocket.com/blog /сообщения/   -  person Devon    schedule 17.07.2018
comment
@Девон отличное чтение. Похоже, он рекомендует подход Exclusive Belongs To. О чем упомянул Илья Бурсов. Большое спасибо!   -  person Billy Stalnaker    schedule 17.07.2018
comment
@BillyStalnaker да, это одинаково для всех решений, с одной полиморфной таблицей вам придется читать ее несколько раз, с отдельными таблицами вы тоже будете читать их все (в сумме они равны одной таблице)   -  person Iłya Bursov    schedule 17.07.2018


Ответы (2)


Могут быть реализованы полиморфные отношения, которые решают проблему присоединения «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)
);

Запрос для получения списка контроля доступа для документа с идентификатором: 42 будет выглядеть следующим образом:

select *
from acl
where resource_type='document'
  and resource_id=42;

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


Метод Exclusive Belongs To — хороший метод повышения ссылочной целостности, но для каждого возможного типа объекта потребуется новый столбец. Цитата из статьи:

Exclusive Belongs To (также известный как 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 у ресурса. Также важны частичные уникальные индексы. Ограничение уникальных индексов только ненулевыми значениями значительно экономит место, а также сокращает количество операций записи при вставке.

person Devon    schedule 17.07.2018
comment
B-E-A-utiful Большое спасибо! - person Billy Stalnaker; 17.07.2018

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

person Alberto Vidales    schedule 17.07.2018