Проектирование базы данных: два отношения «один ко многим» к одной и той же таблице

20

Мне нужно смоделировать ситуацию, когда у меня есть таблица Chequing_Account (которая содержит бюджет, номер iban и другие данные счета), которая должна быть связана с двумя разными таблицами Person и Corporation, которые могут иметь 0, 1 или несколько чековых счетов.

Другими словами, у меня есть два отношения «один ко многим» с одной и той же таблицей.

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

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

2) Создайте две таблицы ссылок PersonToChequingAccount и CorporationToChequingAccount, которые связывают две сущности с проверочными счетами. Однако я не хочу, чтобы два человека имели один и тот же аккаунт для проверки, и я не хочу, чтобы физическое лицо и корпорация имели общий счет для проверки! увидеть это изображение

http://i41.tinypic.com/35i6kbk.png

3) Создайте два внешних ключа в Учетной записи Chequing, которые указывают на Корпорацию и Физическое лицо, однако я бы, таким образом, обеспечил, чтобы Лицо и Компания могли иметь много учетных записей Chequing, однако мне пришлось бы вручную убедиться, что для каждой строки ChequingAccount не оба отношения указывают на Корпорация и Физическое лицо, потому что чековый счет принадлежит корпорации или Физическому лицу. увидеть это изображение

http://i40.tinypic.com/1rpv9z.png

Есть ли другое, более чистое решение этой проблемы?

dendini
источник
Вы думали о том, чтобы, например, OwnerTypeIDв ChecquingAccountтаблице, с 1=Corporationи 2=NaturalPerson? Таким образом, вам нужен только один OwnerIDэлемент в ChecquingAccountтаблице, который вы можете индексировать вместе с OwnerTypeID.
RoKa
Мне нужно не только знать, корпорация это или физическое лицо, но и знать соответствующий идентификатор, поэтому мне нужен номер идентификатора, а не только значение 1 или 2! Решение 3 - то, что я нашел здесь, у меня есть две колонки с идентификаторами корпорации или физического лица
dendini
2
Да, решение является допустимым вариантом. В большинстве СУБД вы можете обеспечить, чтобы только один из двух FK был «активным» с проверочным ограничением: CHECK (CorporationID IS NOT NULL AND NaturalPersonID IS NULL OR CorporationID IS NULL AND NaturalPersonID IS NOT NULL)хотя я предпочитаю решение 1 (но это только я). Это намного "чище".
ypercubeᵀᴹ
Да, я понимаю, но вы могли бы иметь в ChecquingAccountтаблице запись OwnerTypeID=1и OwnerID=123, указывающую, что это тип Corporation, следовательно, идентификатор 123в Corporationтаблице. OwnerTypeID сообщает вам, какая таблица, а OwnerID сообщает вам идентификатор в этой таблице.
RoKa
1
Как вариант № 1 невозможен? В конце концов, слово «корпорация» означает «бизнес, который юридически является человеком». Назовите это Customersстол.
Джон на все руки

Ответы:

15

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

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

Проблема в том, что некоторые из этих целей конкурируют друг с другом.

Решение для подтипа
Вы можете выбрать решение для подтипа, в котором вы создаете супертип, в который входят как корпорации, так и отдельные лица. Этот супертип, вероятно, будет иметь составной ключ натурального ключа подтипа плюс атрибут разделения (например customer_type). Это нормально в том, что касается нормализации, и позволяет вам обеспечивать ссылочную целостность, а также ограничение взаимной исключительности корпораций и отдельных лиц. Проблема в том, что это делает поиск данных более сложным, потому что вы всегда должны переходить в зависимости от того, customer_typeкогда вы присоединяете учетную запись к владельцу учетной записи. Это, вероятно, означает использование UNIONи наличие большого количества повторяющихся SQL в вашем запросе.

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

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

Решение «денормализованных» атрибутов разделения
Существует еще один вариант, когда вы сохраняете один столбец внешнего ключа в таблице контрольного счета и используете другой столбец, чтобы сообщить вам, как интерпретировать столбец внешнего ключа (RoKa'sOwnerTypeIDколонка). Это по существу исключает таблицу супертипа в решении подтипирования путем денормализации атрибута разделения на дочернюю таблицу. (Обратите внимание, что это не является строго «денормализацией» в соответствии с формальным определением, потому что атрибут разделения является частью первичного ключа.) Это решение кажется довольно простым, поскольку оно позволяет избежать использования дополнительной таблицы для более или менее одинакового действия, и сокращает количество столбцов внешнего ключа до одного. Проблема с этим решением состоит в том, что оно не избегает ветвления логики поиска и, более того, оно не позволяет поддерживать декларативную ссылочную целостность. Базы данных SQL не имеют возможности управлять одним столбцом внешнего ключа для одной из нескольких родительских таблиц.

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

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

Джоэл Браун
источник
Мое решение № 2 к какой из ваших четырех групп принадлежит? «Денормализованное решение атрибутов разбиения» мне не совсем понятно ..
dendini
@dendini - Ваше решение № 2 не соответствует ни одному из решений, которые я изложил. Это потому, что это не соответствует требованию учетной записи, принадлежащей одному юридическому лицу. То, как вы определили первичные ключи промежуточных таблиц, это пересечение многих ко многим. Если бы первичные ключи были правильными, corporation_id и person_idтогда у вас, по сути, было бы решение для подтипа, за исключением того, что таблица супертипов была бы разделена на две части, а внешний ключ был бы инвертирован, чтобы люди не могли иметь несколько учетных записей. Этот вид побеждает цель.
Джоэл Браун
Отличное объяснение. @JoelBrown, как это влияет на производительность решения «Два внешних ключа» с точки зрения запросов? Кроме того, учитывая, что вместо 2 может быть 6 или более внешних ключей: вы все равно порекомендовали бы этот подход или скорее склонялись к другому?
Амадео Галлардо
1
@AmadeoGallardo Ответ "это зависит". Запрос к ключу всегда довольно эффективен, так как вы можете рассчитывать, по крайней мере, на сканирование индекса, если не на поиск, и это быстрые операции. Проблема возникает, когда вы запрашиваете оба ключа в решении с двумя внешними ключами . Здесь вы просите оптимизатор запросов выполнить операцию либо. В лучшем случае это удвоит стоимость запроса, обычно немного хуже, так как вам нужно выполнить запрос по одному ключу, затем другому, а затем объединить результаты.
Джоэл Браун
@JoelBrown Денормализованные будущие версии SQL должны позволять этот подход, позволяя определять составной внешний ключ на основе двух столбцов RefIDи RefTableгде RefTableэто фиксированный идентификатор, который идентифицирует целевую таблицу. Существует много вариантов использования этого типа ключа, и очень важно поддерживать 10 или более таблиц связей / подтипов для обеспечения целостности. Для тех случаев я создал это keyсам.
djmj