Devo adicionar chaves estrangeiras transitivas?

11

Exemplo simples: existe uma tabela de clientes.

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

Todos os outros dados no banco de dados devem estar vinculados a um Customer, então, por exemplo, Ordersfica assim:

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

Suponha que agora exista uma tabela com links para 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)
)

Devo adicionar uma chave estrangeira separada de Itemspara Customers?

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

Uma imagem: devo adicionar a linha tracejada / FK?

Esquema de exemplo simples


Editar: adicionei definições de chave primária às tabelas. Gostaria de reiterar o que afirmei acima: o banco de dados é basicamente isolado pelos clientes, como uma medida de correção / segurança. Portanto, todas as chaves primárias contêm o customerID.

vektor
fonte
2
Não, você não deveria. Não há necessidade do FK extra. A restrição é imposta pelos outros dois FKs.
precisa saber é o seguinte
@ypercube Existem penalidades de desempenho por ter um FK redundante? Quaisquer vantagens que você poderia pensar ...?
vektor
1
@vektor, os aspectos de desempenho provavelmente variam de um rdbms para outro, mas geralmente você obtém um impacto no desempenho para cada novo FK adicionado, porque cada inserção / atualização / exclusão em uma das tabelas PK / FK deve ser comparada com o limitação. Com tabelas PK grandes, essa penalidade de desempenho pode ser bastante severa.
Daniel Hutmacher

Respostas:

6

Eu acho que essa é a ideia original.

insira a descrição da imagem aqui

A primeira coisa a notar é que a PK na tabela LineItem possui três atributos {CustomerID, CustomerOrderNo, OdrerItemNo}, em vez de apenas dois no seu exemplo.

A segunda coisa a observar é a confusão resultante do uso do idnome genérico para um atributo.

O CustomerOrderNodeve ser idealmente (1,2,3 ..) para cada cliente e OrderItemNo(1,2,3 ...) para cada ordem.

Bem, isso é bom, se possível, mas requer uma consulta procurando o valor máximo anterior, como

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

que geralmente não é preferido em ambientes de alto volume de transações, por isso é comum vê-los substituídos por um incremento automático, essencialmente servindo ao mesmo objetivo. É verdade que esse incremet automático agora é exclusivo, portanto, pode ser usado como uma CHAVE - mas você pode optar por considerá-lo como um compromisso necessário para o OrderItemNo.

Então, com algumas renomeações CustomerOrderNo -> OrderNoe OrderItemNo-> ItemNovocê pode chegar a esse modelo

insira a descrição da imagem aqui

Então agora, se você olhar para Ordero seguinte, é único

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

Observe que {CustomerID, OrderNo}é propagado para o LineItempara servir como um FK.

Se você apertar os olhos um pouco, isso é parecido com o seu exemplo, mas com PKs {ItemNo} and {OrderNo}apenas - em oposição a duas PKs de coluna do seu exemplo.

Agora, a pergunta é: por que não simplificar para algo assim?

insira a descrição da imagem aqui

Que é bom, mas introduz PATH DEPENDÊNCIA - você não pode juntar-se LineItemcom Customerdiretamente, deve usar Orderna junção.


Prefiro o primeiro caso, quando possível - você escolhe o seu favorito. E, obviamente, não há necessidade de FK direto de LineItempara Customernesses três casos.

Damir Sudarevic
fonte
Eu procurei ao redor, mas eu não posso ver "path dependence" sendo usado como um termo amplamente adotado para o que eu chamo de "2nd relação grau transitivo" (embora minha terminologia é provavelmente incorreta também)
Dai
2

O "item" não deve fazer referência direta ao "cliente", porque isso está implícito no "pedido" do item. Portanto, você não precisará das colunas "customer" na tabela "items".

A relação do item com o cliente é garantida com a chave estrangeira existente.

Se orders.id for uma coluna de identidade, considere remover items.customer no total.

Daniel Hutmacher
fonte
1
Obrigado, não notei que "cliente" também foi incluído no primeiro FK de "itens" a "pedidos". Eu elaborei minha resposta de acordo.
Daniel Hutmacher
@DanielHutmacher Editei a pergunta para conter as chaves primárias das minhas tabelas. Isso explica os FKs estranhos que você mencionou na sua edição.
vektor
Ok, eu atualizei minha resposta. :)
Daniel Hutmacher
Eu estou supondo que ter customerem todas as tabelas (e assim silenciar o DB) é uma abordagem incomum. Devo confessar que é apenas algo que vi no meu trabalho anterior. Isso faz algum sentido para você? Você já viu esse design antes?
vektor
Eu diria que parece uma abordagem de datawarehouse (esquema em estrela), na qual você deseja desnormalizar intencionalmente os dados para eliminar junções. Ou a chave primária pode ser composta, ou seja, primeiro, segundo, terceiro pedido do cliente A, primeiro, segundo pedido do cliente B etc. - quando a coluna de identificação do pedido sozinha não é única.
Daniel Hutmacher