Должен ли я добавить транзитивные внешние ключи?

11

Простой пример: есть таблица клиентов.

create table Customers (
  id integer,
  constraint CustomersPK primary key (id)
)

Все остальные данные в базе данных должны быть связаны с Customer, например, Ordersтак:

create table Orders (
  id integer,
  customer integer,
  constraint OrdersPK primary key (customer, id),
  constraint OrdersFKCustomers foreign key (customer) references Customers (id)
)

Предположим, теперь есть таблица, ссылающаяся на Orders:

create table Items (
  id integer,
  customer integer,
  order integer,
  constraint ItemsPK primary key (customer, id),
  constraint ItemsFKOrders foreign key (customer, order) references Orders (customer, id)
)

Должен ли я добавить отдельный внешний ключ от Itemsк Customers?

...
constraint ItemsFKCustomers foreign key (customer) references Customers (id)

Картина вместо этого: я должен добавить пунктирную линию / FK?

Простой пример схемы


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

Vektor
источник
2
Нет, ты не должен Нет необходимости в дополнительных FK. Ограничение применяется двумя другими ФК.
ypercubeᵀᴹ
@ypercube Есть ли какие-либо потери производительности за наличие избыточного FK? Есть ли у вас какие-либо преимущества? ...
вектор
1
@vektor, аспекты производительности, вероятно, варьируются от одного rdbms к другому, но, как правило, вы получаете влияние на производительность для каждого нового добавляемого FK, потому что каждая вставка / обновление / удаление в одной из таблиц PK / FK должна проверяться на соответствие ограничение. С большими таблицами PK это снижение производительности может быть довольно серьезным.
Даниэль Хатмахер

Ответы:

6

Я думаю, что это оригинальная идея.

введите описание изображения здесь

Первое, на что нужно обратить внимание, это то, что PK в таблице LineItem имеет три атрибута {CustomerID, CustomerOrderNo, OdrerItemNo}, а не два в вашем примере.

Второе, на что следует обратить внимание, - это путаница, возникшая из-за использования общего idимени для атрибута.

В CustomerOrderNoидеале должно быть (1,2,3 ..) для каждого клиента и OrderItemNo(1,2,3 ...) для каждого заказа.

Ну, это хорошо, если это возможно, но требует, чтобы запрос искал предыдущее максимальное значение, например

select max(CustomerOrderNo)
from Order 
where CustomerID = specific_customer ; 

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

Итак, с некоторым переименованием CustomerOrderNo -> OrderNoи OrderItemNo-> ItemNoвы можете прийти к этой модели

введите описание изображения здесь

Так что теперь, если вы посмотрите на Orderследующие уникальные

{OrderNo}             -- PK
{CustomerID, OrderNo} -- superkey,  AK on the diagram.

Обратите внимание, что {CustomerID, OrderNo}распространяется на LineItemв качестве FK.

Если вы немного покоситесь, это близко к вашему примеру, но PKs {ItemNo} and {OrderNo}только - в отличие от двух колонок PK из вашего примера.

Теперь вопрос, почему бы не упростить что-то подобное?

введите описание изображения здесь

Что хорошо, но вводит PATH ЗАВИСИМОСТЬ - вы не можете присоединиться LineItemс Customerнепосредственно, должны использовать Orderв соединении.


Я предпочитаю первый случай, когда это возможно - вы выбираете свой любимый. И, очевидно, в этих трех случаях нет необходимости в прямом ФК от LineItemдо Customer.

Дамир Сударевич
источник
Я искал вокруг, но я не вижу, чтобы «зависимость от пути» использовался в качестве широко принятого термина для того, что я бы назвал «переходные отношения 2-й степени» (хотя моя терминология, вероятно, тоже неверна)
Дай
2

«Товар» не должен напрямую ссылаться на «клиента», потому что это подразумевается «заказом» товара. Таким образом, вам не понадобятся столбцы "customer" в таблице "items".

Отношение товара к покупателю обеспечивается существующим внешним ключом.

Если orders.id является столбцом идентификаторов, рассмотрите возможность полного удаления items.customer.

Даниэль Хутмахер
источник
1
Спасибо, я не заметил, что "клиент" также был включен в первый FK от "товаров" до "заказов". Я разработал свой ответ соответственно.
Даниэль Хутмахер
@DanielHutmacher Я отредактировал вопрос, чтобы он содержал первичные ключи моих таблиц. Это объясняет странные ФК, которые вы упоминаете при редактировании.
вектор
Хорошо, я обновил свой ответ. :)
Даниэль Хутмахер
Я предполагаю, что наличие customerво всех таблицах (и, следовательно, бункера БД) является необычным подходом. Я должен признаться, что это просто то, что я видел в моей предыдущей работе. Это имеет какой-то смысл для вас? Вы видели такой дизайн раньше?
вектор
Я бы сказал, что это похоже на подход хранилища данных (звездная схема), где вы хотите преднамеренно денормализовать данные, чтобы исключить объединения. Или первичный ключ может быть составным, то есть первый, второй, третий заказ клиента A, первый, второй заказ клиента B и т. Д., Если только столбец идентификатора заказа не является уникальным.
Даниэль Хутмахер