Несовпадения внешнего ключа нескольких столбцов - PullRequest
6 голосов
/ 23 декабря 2010

Я хочу настроить ограничения таблиц для следующего сценария, и я не уверен, как это сделать или вообще возможно ли это в SQL Server 2005.

У меня есть три таблицы A, B, C. C является дочерним по отношению к B. B будет иметь необязательный внешний ключ (может быть нулевым), ссылающийся на A. Из соображений производительности я также хочу, чтобы таблица C имела такую ​​же ссылку на внешний ключ к таблице A. Ограничение для таблицы C должно быть таким, что C должен ссылаться на своего родителя (B), а также иметь ту же ссылку внешнего ключа на A, что и его родитель.

У кого-нибудь есть мысли, как это сделать?

Ответы [ 5 ]

6 голосов
/ 23 декабря 2010

Я не вижу необходимости явно применять отношения от C к A. Просто следуйте по цепочке от C к B к A.

2 голосов
/ 23 декабря 2010

В общем, я не вижу конкретной причины для этого - однако вы спросили.

Важно понимать, что реляционная модель не должна следовать модели ОО.Это стандартный способ представления Customer-Order-LineItem.В этом нет ничего плохого.

alt text

Если я хочу найти все позиции, принадлежащие клиенту, я должен присоединиться через таблицу Order, аналогично точке OO-точечная нотация (Customer.Order.LineItem).

select * 
from Customer as c
join Order    as o on o.CustomerId = c.CustomerId
join LineItem as i on i.OrderId    = o.OrderId
where CustomerID = 7 ;

Предположим, что я немного модифицирую ключи, например:

alt text

CustomerOrderId - это порядокпорядковый номер для каждого клиента (1,2,3 ...), а CustomerOrderItemId - порядковый номер отдельной позиции для каждого из заказов клиента (1,2,3 ...).Каждую из них легко создать, как в

-- next CustomerOrderId
select coalesce(max(CustomerOrderId), 0) + 1
from  Order
where CustomerId = specific_customer_id;

-- next CustomerOrderItemId
select coalesce(max(CustomerOrderItemId), 0) + 1
from  LineItem
where CustomerId      = specific_customer_id
  and CustomerOrderId = specific_customer_order_id;

Теперь, если я хочу найти позиции, принадлежащие клиенту (и некоторые данные о клиентах), я могу пропустить таблицу Order.

select * 
from Customer as c
join LineItem as i on i.CustomerId = c.CustomerId
where CustomerID = 7 ;

И если мне не нужны какие-либо конкретные данные из таблицы Customer, присоединяться вообще не нужно.Сравните это с первым примером - помните, что целью было получить позиции.

select * 
from LineItem
where CustomerID = 7 ;

Итак, с реляционной моделью, распространяя (естественные) ключи, вы не должны всегда "останавливаться накаждая станция на пути связи "в соединениях.

Что лучше?Зависит от того, кого вы спрашиваете.

Надеюсь, вы сможете воплотить основной принцип в своем примере - мне трудно работать с универсальным (A, B, C).

1 голос
/ 23 декабря 2010

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

Задумывались ли вы просто о наличии триггера вставки на C, который устанавливает ссылку на столбец таблицы A на основе поиска в таблице B?Вам также могут потребоваться триггеры обновления на C и B, чтобы обеспечить их синхронизацию.Это гарантирует, что столбец в таблице C, который ссылается на таблицу A, всегда корректен, даже если он не «принудительно применяется» фактическим ограничением.

0 голосов
/ 23 декабря 2010

Похоже на то, что у вас есть ключ A, и вам нужны все соответствующие строки в C. В этом случае следующий запрос должен быть быстрым:

select C.* 
from B
join C on C.Bid = B.Bid
where C.Aid = <value>

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

0 голосов
/ 23 декабря 2010

У меня есть три таблицы A, B, C.C является дочерним по отношению к B. B будет иметь необязательный внешний ключ (может быть нулевым), ссылающийся на A. Из соображений производительности я также хочу, чтобы таблица C имела такую ​​же ссылку на внешний ключ к таблице A. Ограничение для таблицы C должно быть таким, что Cдолжен ссылаться на своего родителя (B), а также иметь ту же ссылку внешнего ключа на A, что и его родитель., а затем используйте это для ссылки на C. Это не позволит вам иметь нулевую ссылку на внешний ключ на B, но внешние ключи в любом случае не могут быть нулевыми.

На самом деле, если у вас правильно настроены индексы и т. Д., Нет особой необходимости нажимать клавишу A на C. Соединение с таблицей B для получения ключа A не будет таким уж большим ударом по производительности (вроде почти нет).

...