SQL - clé primaire de table plusieurs à plusieurs

125

Cette question se pose après la lecture d'un commentaire dans cette question:

Conception de base de données

Lorsque vous créez une table plusieurs-à-plusieurs, devez-vous créer une clé primaire composite sur les deux colonnes de clé étrangère, ou créer une clé primaire «ID» de substitution à incrémentation automatique, et simplement mettre des index sur vos deux colonnes FK (et peut-être une contrainte unique)? Quelles sont les implications sur les performances de l'insertion de nouveaux enregistrements / de la réindexation dans chaque cas?

Fondamentalement, ceci:

PartDevice
----------
PartID (PK/FK)
DeviceID (PK/FK)

vs ceci:

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)

Le commentateur dit:

faire des deux ID le PK signifie que la table est physiquement triée sur le disque dans cet ordre. Donc, si nous insérons (Part1 / Device1), (Part1 / Device2), (Part2 / Device3), alors (Part 1 / Device3) la base de données devra séparer la table et insérer la dernière entre les entrées 2 et 3. Pour beaucoup d'enregistrements, cela devient très problématique car cela implique de mélanger des centaines, des milliers ou des millions d'enregistrements chaque fois qu'un est ajouté. En revanche, un PK à auto-incrémentation permet aux nouveaux enregistrements d'être cloués à la fin.

La raison pour laquelle je pose la question est que j'ai toujours été enclin à utiliser la clé primaire composite sans colonne d'incrémentation automatique de substitution, mais je ne suis pas sûr que la clé de substitution soit réellement plus performante.

Andy White
la source
Voici une question silimar postée sur SO: stackoverflow.com/questions/344068/…
Tony
(J'ai essayé d'ajouter cela à mon commentaire précédent mais je ne peux pas) En fonction du nombre d'insertions, vous pouvez également reconstruire périodiquement votre index pour vous assurer qu'il renvoie rapidement des résultats. Dans SQL Server, vous pouvez également modifier le FILLFACTOR de l'index pour fournir suffisamment d'espace pour les insertions avant de devoir déplacer des données.
Tony
1
La réponse à cette question ne dépend-elle pas du SGBD utilisé? Je soupçonne que MySQL se comportera d'une certaine manière dans ce cas, SQL-Server légèrement d'une autre manière, etc.
Radu Murzea
Attention: sans une balise de base de données spécifique, une grande partie de ce qui est dit ici est suspect. Différents moteurs fonctionnent différemment!
Rick James le

Réponses:

85

Avec un simple mappage plusieurs à plusieurs à deux colonnes, je ne vois aucun avantage réel à avoir une clé de substitution. Avoir une clé primaire sur (col1,col2)est garanti unique (en supposant que vos valeurs col1et col2dans les tables référencées sont uniques) et un index séparé sur (col2,col1)détectera les cas où l'ordre opposé s'exécuterait plus rapidement. Le substitut est un gaspillage d'espace.

Vous n'aurez pas besoin d'index sur les colonnes individuelles car la table ne doit être utilisée que pour joindre les deux tables référencées ensemble.

Ce commentaire auquel vous faites référence dans la question ne vaut pas, à mon avis, les électrons qu'il utilise. Il semble que l'auteur pense que la table est stockée dans un tableau plutôt que dans une arborescence multi-voies équilibrée extrêmement performante.

Pour commencer, il n'est jamais nécessaire de stocker ou d'obtenir à la table triée, juste l'index. Et l'index ne sera pas stocké séquentiellement, il sera stocké de manière efficace pour pouvoir être récupéré rapidement.

De plus, la grande majorité des tables de base de données sont lues beaucoup plus souvent qu'écrites. Cela rend tout ce que vous faites du côté de la sélection beaucoup plus pertinent que tout ce que vous faites du côté de l'insert.

paxdiablo
la source
Le dernier point n'est pas une bonne généralisation: "la grande majorité des tables de la base de données sont lues beaucoup plus souvent qu'écrites". Je trouve de nombreux exemples de tables associatives qui doivent être écrites très souvent, par exemple une table liant le client à la commande.
utilisateur
5
@buffer, je maintiens ce commentaire (techniquement, c'est une généralisation seulement si je dis "toutes les tables", "la grande majorité" est basée sur l'expérience). Pensons également à votre exemple, une commande est créée une fois (elle peut être mise à jour occasionnellement, mais il est peu probable qu'elle modifie les informations de clé / d'index, plus pour atteindre des choses comme l'état de la commande. Cependant, ces mises à jour et les sélections que vous devrez faire pour imprimer des factures ou générer des rapports de gestion l'emportera sur l'encart d'origine.
paxdiablo
Pensez à Amazon - Des milliers de commandes créées chaque heure.
utilisateur
9
@buffer, oui, mais encore une fois, chacune de ces commandes sera presque certainement interrogée à plusieurs reprises pour faire (par exemple) de l'emballage, de la facturation, des mises à jour de statut, des analyses commerciales, etc. Le nombre absolu de créations est moins important que le rapport entre les créations et les lectures.
paxdiablo
1
Mon point est que insertcela importera si cela est fait des milliers de fois par heure. Vous ne pouvez pas simplement l'ignorer simplement parce que le rapport de insertà selectest <1. ​​Dans ce cas, un client se soucie du temps qu'il faut pour passer une commande.
utilisateur
19

Aucune clé de substitution n'est nécessaire pour les tables de liens.

Un PK sur (col1, col2) et un autre index unique sur (col2, col1) est tout ce dont vous avez besoin

Sauf si vous utilisez un ORM qui ne peut pas faire face et qui vous dicte la conception de votre base de données ...

Edit: J'ai répondu à la même chose ici: SQL: Avez-vous besoin d'une clé primaire auto-incrémentielle pour les tables Many-Many?

gbn
la source
3
Vous pourriez être d'accord avec un index dups sur col2 au lieu d'un index unique sur (col2, col1). L'avantage de l'index à deux colonnes est qu'il permet des analyses d'index uniquement sur col2 seul ou à la fois sur col1 et col2 (bien que l'autre index, sur (col1, col2) gère également le cas «both»). L'inconvénient est le stockage supplémentaire nécessaire pour la colonne supplémentaire. Ce n'est généralement pas significatif, donc le conseil est loin d'être horrible. Néanmoins, si col1 et col2 sont gros ou de tailles très différentes, vous pouvez économiser de l'espace sans nuire aux performances en choisissant d'avoir le deuxième index sur la colonne la plus courte.
Jonathan Leffler
@gbn: Le deuxième index sur (col2, col1) n'a pas besoin d'être unique, non?
utilisateur
1
mettre un index unique sur (col1, col2) après qu'il soit déjà un PK est totalement redondant
Don Cheadle
@mmcrae: où fait-on ça?
gbn
2
@mmcrae: Votre commentaire est "mettre un index unique sur (col1, col2) ..". L'ordre des colonnes dans un index est important. (col2, col1)n'est pas (col1, col2). Le PK de (col1, col2)peut ne pas convenir à toutes les requêtes et générer des analyses, donc avoir l'inverse de cela améliore les performances car il permet de rechercher où col2 est meilleur. Par exemple, la validation FK lorsque la table avec col2 a une suppression. La table enfant smuts être vérifiée
GBN
12

Une clé primaire incrémentielle peut être nécessaire si la table est référencée. Il peut y avoir des détails dans la table plusieurs à plusieurs qui doivent être extraits d'une autre table à l'aide de la clé primaire incrémentielle.

par exemple

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)
Other Details

Il est facile d'extraire les «Autres détails» en utilisant PartDevice.ID comme FK. Ainsi, l'utilisation d'une clé primaire incrémentielle est nécessaire.

Jronny
la source
1
Merci! Je suis venu à la réponse car je cherchais presque le même scénario que vous avez décrit. Mais vous vous êtes éloigné de votre première phrase en ajoutant "Autres détails". Et si j'avais une table de mappage plusieurs à plusieurs, à laquelle je dois me référer à partir d'une autre table? Cela signifie que la table de mappage plusieurs à plusieurs n'a stocké aucune autre information ... La colonne ID supplémentaire aurait-elle un sens de toute façon? Sinon, comment faire référence à un enregistrement de la table de mappage à la place?
misanthrop
Il y a deux options ici, vous pouvez utiliser la clé composée comme clé étrangère de votre table de référence (cela ajoute une colonne supplémentaire à votre nouvelle table), ou vous pouvez créer une colonne id dans la table de mappage et définir une contrainte unique sur le composé d'origine clé primaire tandis que la nouvelle colonne d'identifiant deviendra la clé primaire.
Vočko
6

La manière la plus courte et la plus directe de répondre à votre question est de dire qu'il y aura un impact sur les performances si les deux tables que vous liez n'ont pas de clés primaires séquentielles. Comme vous l'avez dit / cité, l'index de la table de liens deviendra fragmenté ou le SGBD travaillera plus dur pour insérer des enregistrements si la table de liens ne possède pas sa propre clé primaire séquentielle. C'est la raison pour laquelle la plupart des gens mettent une clé primaire à incrémentation séquentielle sur les tables de liens.

Bernhard Hofmann
la source
2

Il semble donc que si le seul travail consiste à lier les deux tables, le meilleur PK serait le PK à deux colonnes.

Mais si cela sert à d'autres fins, ajoutez un autre NDX en tant que PK avec une clé étrangère et un deuxième index unique.

L'index ou PK est le meilleur moyen de s'assurer qu'il n'y a pas de doublons. PK laisse des outils comme Microsoft Management Studio faire une partie du travail (création de vues) pour vous

Michael Kosak
la source