Une colonne d'ID unique est-elle nécessaire dans une table plusieurs à plusieurs (jonction)?

22

Faire démarrer quelques projets avec EF, mais j'avais quelques questions sur les tables de jointure et les clés, etc. Disons que j'ai une table d'applications et une table d'autorisations. Les applications ont de nombreuses autorisations et chaque autorisation peut appartenir à de nombreuses applications (plusieurs à plusieurs).

Maintenant, les tables d'application et d'autorisation sont faciles:

Applications
--------------
PK  ApplicationID
    Name

Permissions
--------------
PK  PermissionID
    Name

Mais quelle est la MEILLEURE façon de faire la table de jointure? J'ai ces deux options:

ApplicationPermissions
-----------------------
PK  ApplicationPermissionID
CU  ApplicationID
CU  PermissionID

OU

ApplicationPermissions
-----------------------
CPK ApplicationID
CPK PermissionID

PK = Primary Key
CPK = Composite Primary Key
CU = Composite Unique Index

Avez-vous déjà été brûlé en le faisant d'une manière par rapport à l'autre? est-ce strictement une préférence? Il m'est venu à l'esprit que beaucoup de "différences" seront résumées par mon modèle de référentiel (par exemple, je ne créerais presque jamais un objet d'autorisation entier et l'ajouterais à une application, mais le faire par ID ou nom unique ou quelque chose), mais je suppose que je cherche des histoires d'horreur, d'une manière ou d'une autre.

solidau
la source

Réponses:

20

Je pense que vous voulez dire une table de "jonction", pas une table de "jointure".

Il n'est pas nécessaire qu'une table de jonction ait son propre champ ID. Vous n'auriez jamais besoin de rejoindre ou de filtrer sur un tel ID. Vous souhaitez uniquement joindre ou filtrer les ID des tables que vous mappez. Un ID sur une table de jonction est un gaspillage d'espace disque.

Donc, la "meilleure" option est d'éviter l'ID. En règle générale, une table de jonction aura 2 index de recouvrement. Chaque index de couverture utilisant l'un des ID mappés comme champ de tri principal.

Mais "le meilleur" n'est pas de loin. C'est un problème très mineur d'avoir un champ ID redondant. Vous n'aurez pas d'histoires d'horreur sur une petite quantité de disque gaspillé. L'ID ne "volera" pas l'index clusterisé, car vous ne voulez de toute façon pas clusteriser sur le combo mappé.

Si votre framework veut que toutes les tables aient un ID, allez-y. Si les normes de base de données de votre équipe dictent que toutes les tables doivent avoir un ID, allez-y. Sinon, évitez-le.

mike30
la source
2
Eh bien, vous avez déjà déclaré que l'ajout d'un ID est une concession mineure, facilement surmontée par les avantages potentiels, il me semble donc que (étant donné qu'avoir un ID unique dans chaque table est plus ou moins la meilleure pratique dans la plupart des SGBD et ORM) vous recommanderiez d' avoir un ID comme option "meilleure" ou "par défaut", plutôt que de ne pas en avoir un.
Robert Harvey
4
"Vous n'auriez jamais besoin de joindre ou d'interroger sur un tel ID" - dire "jamais" dans une situation technologique invite cette chose à se produire. Cela dit, il y a des moments où vous rejoindrez cette table de jointure (oui, je l'ai entendu parler de table de «jointure» plus que de table de «jonction») pour encore une quatrième table parce que les entités jointes sont en fait un objet commercial propre.
Jesse C. Slicer
4
@RobertHarvey. Un ID est une bonne pratique pour les entités. Mais une jonction est plus un détail d'implémentation pour les relations plusieurs-plusieurs, pas une entité à part entière. Mais comme le souligne Jesse C. slider, il y a des cas où une jonction pourrait être considérée comme une entité commerciale.
mike30
1
"gaspillage d'espace disque." - Je pense que certains moteurs (InnoDB?) Créent de toute façon une clé primaire (interne) si vous n'en créez pas vous-même - il est donc possible que vous ne gagniez pas réellement d'espace disque en n'en ayant pas.
Alex
@Alex. Vous mettez un PK composite sur les ID mappés.
mike30
11

Au fil des ans, j'ai pris l'habitude de donner à chaque table "TableName" une clé primaire générée automatiquement "TableNameID", sans aucune exception, pas même pour les tables de jonction. Je peux dire que je ne l'ai jamais regretté, car cela facilite beaucoup de choses lors de la création de code générique qui fait quelque chose pour "toutes les tables" ou "certaines tables", ou pour "beaucoup de lignes de plusieurs tables différentes".

Par exemple, si quelqu'un vous demande de stocker des lignes de tables différentes (ou des références à celles-ci) dans un fichier ou en mémoire, par exemple, à des fins de journalisation, il est très pratique lorsque vous savez au préalable que vous avez juste besoin d'en stocker exactement une nom de la table et exactement un ID entier, et vous n'avez pas à vous occuper de "cas spéciaux".

Une autre chose, lorsque vous commencez avec des PK combinés, vous rencontrerez probablement quelques fois plus tard le besoin de clés étrangères combinées (car vous pouvez arriver à un point où vous souhaitez ajouter une référence FK à votre ApplicationPermissionstable). Ensuite, la prochaine exigence peut être que ce FK soit unique en conjonction avec d'autres attributs ou clés étrangères - ce qui entraînera une complexité globale accrue. Rien de ce qui n'est pas possible à gérer pour la plupart des systèmes DB modernes, bien sûr, mais une solution uniforme facilite souvent la vie des programmeurs.

Et enfin, une déclaration comme SELECT ... FROM TABLE WHERE TableNameID IN (id1,id2,...)ça fonctionne bien avec une seule colonne comme clé primaire, mais je n'ai jamais vu de dialecte SQL jusqu'à présent qui vous permet de le faire avec des clés combinées. Si vous savez à l'avance que vous n'aurez jamais besoin d'une requête comme celle-ci, très bien, mais ne soyez pas surpris si demain vous obtenez une exigence qui sera résolue le plus facilement avec ce type de SQL.

Bien sûr, lorsque vous vous attendez à ce que votre ApplicationPermissionstable contienne plusieurs centaines de millions de lignes, vous devriez alors éviter quelque chose comme a ApplicationPermissionsID.

Doc Brown
la source
Même si je n'ai pas fini par choisir votre réponse. J'aime bien certains aspects. Merci pour vos pensées (vote positif).
solidau
6

Bien que la réponse de Mike soit bonne, voici les raisons pour lesquelles j'ajouterais un champ ID distinct ou non.

  1. Envisagez d'utiliser un champ d'ID distinct pour la table de jonction / jointure si elle contient des champs autres que l'ID . Cela tend à noter qu'il s'agit d'une entité de première classe.

  2. Envisagez d'utiliser un champ ID distinct si les API ou toute logique existante ont tendance à utiliser des champs uniques pour récupérer / modifier des entités. Cela peut aider d'autres personnes à suivre votre code dans le cadre d'un projet plus vaste.

  3. Ne l'utilisez pas s'il n'y a aucun avantage spécifique (KISS). EF sait comment gérer ce type de table et une contrainte composite unique peut parfois être manquée lorsque d'autres personnes tentent de comprendre ce type de relation. De plus, lors de la normalisation, j'essaie d'utiliser la plus petite clé possible qui définit de manière unique le tuple . Dans votre deuxième exemple, vous avez effectivement 2 clés primaires candidates distinctes.

Zachary Yates
la source
-5
table Person
   Id int identity(1,1) not null primary key
   ...other fields go here...
table Address
   Id int identity(1,1) not null primary key
   ...other fields go here...
table PersonAddress
   Id int identity(1,1) not null primary key
   PersonId int not null
   AddressId int not null

N'oubliez pas de créer un index et une clé étrangère sur PersonIdet AddressId.

Peu importe ce que les autres pensent être "meilleur" ou "vous devriez", c'est le moyen le plus simple et le plus facile de permettre à la base de données de fonctionner correctement.

16PlusYearsAsADeveloper
la source
1
Je pense que l' un problème avec cette approche est le schéma permet à deux PersonAddresslignes à l' identique PersonIdet les AddressIdvaleurs.
Sam