Dois-je ajouter des clés étrangères transitives?

11

Exemple simple: il y a une table des clients.

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

Toutes les autres données de la base de données doivent être liées à un Customer, par exemple, cela Ordersressemble à ceci:

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

Supposons maintenant qu'il existe une table reliant 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)
)

Dois-je ajouter une clé étrangère distincte de Itemsà Customers?

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

Une image à la place: dois-je ajouter la ligne pointillée / FK?

Exemple de schéma simple


Modifier: j'ai ajouté des définitions de clé primaire aux tables. Je voudrais réitérer sur le point que j'ai soulevé ci-dessus: la base de données est essentiellement cloisonnée par les clients, en tant que mesure d'exactitude / sécurité. Par conséquent, toutes les clés primaires contiennent l' customerID.

vektor
la source
2
Non, tu ne devrais pas. Il n'y a pas besoin de FK supplémentaire. La contrainte est appliquée par les deux autres FK.
ypercubeᵀᴹ
@ypercube Y a-t-il des pénalités de performance pour avoir un FK redondant? Des avantages
auxquels
1
@vektor, les aspects de performance varient probablement d'un rdbms à l'autre, mais généralement, vous obtenez un impact sur les performances pour chaque nouveau FK que vous ajoutez, car chaque insertion / mise à jour / suppression dans l'une des tables PK / FK doit être vérifiée par rapport à la contrainte. Avec de grandes tables PK, cette pénalité de performance peut être assez sévère.
Daniel Hutmacher

Réponses:

6

Je pense que c'est l'idée originale.

entrez la description de l'image ici

La première chose à noter est que le PK sur la table LineItem a trois attributs {CustomerID, CustomerOrderNo, OdrerItemNo}, contre seulement deux dans votre exemple.

La deuxième chose à noter est la confusion résultant de l'utilisation du idnom générique pour un attribut.

L' CustomerOrderNoidéal devrait être (1,2,3 ..) pour chaque client et OrderItemNo(1,2,3 ...) pour chaque commande.

Eh bien, c'est bien si possible, mais nécessite une requête recherchant la valeur maximale précédente, comme

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

ce qui n'est souvent pas préféré dans les environnements à volume de transaction élevé, il est donc courant de les voir remplacés par une incrémentation automatique, servant essentiellement le même objectif. Il est vrai que cet auto-incrément est désormais unique, il peut donc être utilisé comme une clé - mais vous pouvez choisir de le considérer comme un compromis nécessaire pour le OrderItemNo.

Donc, avec quelques renommage CustomerOrderNo -> OrderNoet OrderItemNo-> ItemNovous pouvez arriver à ce modèle

entrez la description de l'image ici

Alors maintenant, si vous regardez Orderce qui suit est unique

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

Notez que {CustomerID, OrderNo}se propage vers le LineItempour servir de FK.

Si vous PKs {ItemNo} and {OrderNo}plissez les yeux , cela est proche de votre exemple, mais avec seulement - par opposition aux PK à deux colonnes de votre exemple.

Maintenant, la question est, pourquoi ne pas simplifier à quelque chose comme ça?

entrez la description de l'image ici

Ce qui est bien, mais introduit PATH DEPENDANCE - vous ne pouvez pas joindre LineItemavec Customerdirectement, devez utiliser Orderdans la jointure.


Je préfère le premier cas lorsque cela est possible - vous choisissez votre préféré. Et évidemment, il n'y a pas besoin de FK direct de LineItemà Customerdans ces trois cas.

Damir Sudarevic
la source
J'ai cherché autour de moi, mais je ne vois pas "dépendance au chemin" utilisé comme un terme largement adopté pour ce que j'appellerais "relation transitive du 2ème degré" (bien que ma terminologie soit probablement incorrecte aussi)
Dai
2

L '"article" ne doit pas référencer directement le "client", car cela est impliqué par la "commande" de l'article. Ainsi, vous n'aurez pas du tout besoin des colonnes "client" de la table "articles".

La relation de l'article avec le client est assurée avec la clé étrangère existante.

Si orders.id est une colonne d'identité, envisagez de supprimer ensemble items.customer.

Daniel Hutmacher
la source
1
Merci, je n'ai pas remarqué que "client" était également inclus dans le premier FK de "articles" à "commandes". J'ai élaboré ma réponse en conséquence.
Daniel Hutmacher
@DanielHutmacher J'ai modifié la question pour qu'elle contienne les clés primaires de mes tables. Cela explique les étranges FK que vous mentionnez dans votre montage.
vektor
Ok, j'ai mis à jour ma réponse. :)
Daniel Hutmacher
Je suppose que le fait d'avoir customerdans toutes les tables (et donc de cloisonner la DB) est une approche inhabituelle. Je dois avouer que c'est juste quelque chose que j'ai vu dans mon travail précédent. Cela a-t-il un sens pour vous? Avez-vous déjà vu un tel design auparavant?
vektor
Je dirais que cela ressemble à une approche de datawarehouse (schéma en étoile), où vous souhaitez dénormaliser intentionnellement les données pour éliminer les jointures. Ou la clé primaire peut être composite, c'est-à-dire la première, la deuxième, la troisième commande du client A, la première, la deuxième commande du client B, etc. - lorsque la colonne d'ID de commande seule n'est pas unique.
Daniel Hutmacher