Quel peut être l'inconvénient de toujours avoir une seule colonne entière comme clé primaire?

18

Dans une application Web sur laquelle je travaille, toutes les opérations de base de données sont abstraites à l'aide de certains référentiels génériques définis sur Entity Framework ORM.

Cependant, afin d'avoir une conception simple pour les référentiels génériques, toutes les tables impliquées doivent définir un entier unique ( Int32en C #, inten SQL). Jusqu'à présent, cela a toujours été le PK de la table et aussi le IDENTITY.

Les clés étrangères sont fortement utilisées et font référence à ces colonnes entières. Ils sont nécessaires à la fois pour la cohérence et pour générer des propriétés de navigation par l'ORM.

La couche application effectue généralement les opérations suivantes:

  • chargement initial des données du tableau (*) -SELECT * FROM table
  • Mise à jour -UPDATE table SET Col1 = Val1 WHERE Id = IdVal
  • Supprimer -DELETE FROM table WHERE Id = IdVal
  • Insérer -INSERT INTO table (cols) VALUES (...)

Opérations moins fréquentes:

  • Insertion enBULK INSERT ... into table masse - suivie (*) de tous les chargements de données (pour récupérer les identifiants générés)
  • Suppression en masse - il s'agit d'une opération de suppression normale, mais "volumineuse" du point de vue de l'ORM:DELETE FROM table where OtherThanIdCol = SomeValue
  • Mise à jour en masse - il s'agit d'une opération de mise à jour normale, mais "volumineuse" du point de vue de l'ORM:UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue

* toutes les petites tables sont mises en cache au niveau de l'application et presque toutes SELECTsn'atteindront pas la base de données. Un modèle typique est la charge initiale et beaucoup de INSERTs, UPDATEs et DELETEs.

Selon l'utilisation actuelle de l'application, il y a très peu de chances d'atteindre 100 millions d'enregistrements dans l'une des tables.

Question: Du point de vue d'un administrateur de base de données, y a-t-il des problèmes importants que je peux rencontrer en ayant cette limitation de conception de table?

[ÉDITER]

Après avoir lu les réponses (merci pour les bons commentaires) et les articles référencés, j'ai l'impression que je dois ajouter plus de détails:

  1. Spécificités de l'application actuelle - Je n'ai pas mentionné l'application Web actuelle, car je veux savoir si le modèle peut également être réutilisé pour d'autres applications. Cependant, mon cas particulier est une application qui extrait de nombreuses métadonnées d'un DWH. Les données sources sont assez désordonnées (dénormalisées de manière étrange, présentant des incohérences, aucun identifiant naturel dans de nombreux cas, etc.) et mon application génère des entités clairement séparées. De plus, de nombreux identifiants générés ( IDENTITY) sont affichés, afin que l'utilisateur puisse les utiliser comme clés professionnelles. Ceci, outre une refactorisation massive du code, exclut l'utilisation des GUID .

  2. "ils ne devraient pas être le seul moyen d'identifier de manière unique une rangée" (Aaron Bertrand ♦) - c'est un très bon conseil. Tous mes tableaux définissent également une CONTRAINTE UNIQUE pour garantir que les doublons d'entreprise ne sont pas autorisés.

  3. Conception orientée application frontale par rapport à la conception basée sur une base de données - le choix de conception est dû à ces facteurs

    1. Limitations d'Entity Framework - plusieurs colonnes PK sont autorisées, mais leurs valeurs ne peuvent pas être mises à jour

    2. Limitations personnalisées - le fait d'avoir une seule clé entière simplifie considérablement les structures de données et le code non SQL. Par exemple: toutes les listes de valeurs ont une clé entière et des valeurs affichées. Plus important encore, il garantit que toute table marquée pour la mise en cache pourra être placée dans une Unique int key -> valuecarte.

  4. Requêtes de sélection complexes - cela ne se produira presque jamais car toutes les données des tables de petite taille (<20 à 30 000 enregistrements) sont mises en cache au niveau de l'application. Cela rend la vie un peu plus difficile lors de l'écriture de code d'application (plus difficile à écrire LINQ), mais la base de données est beaucoup plus agréable:

    1. Vues de liste - ne générera aucune SELECTrequête lors du chargement (tout est mis en cache) ou des requêtes qui ressemblent à ceci:

      SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)

      Toutes les autres valeurs requises sont récupérées via les recherches de cache (O (1)), donc aucune requête complexe ne sera générée.

    2. Modifier les vues - générera des SELECTinstructions comme celle-ci:

      SELECT allcolumns FROM BigTable WHERE PKId = value1

(tous les filtres et valeurs sont ints)

Alexei
la source
Vous pourriez trouver ces publications pertinentes, car certains aspects logiques, physiques et pratiques sont discutés en ce qui concerne l'utilisation de colonnes avec des valeurs de substitution générées par le système.
MDCCL

Réponses:

19

Outre l'espace disque supplémentaire (et à son tour l'utilisation de la mémoire et des E / S), il n'y a pas vraiment de mal à ajouter une colonne IDENTITY même aux tables qui n'en ont pas besoin (un exemple de table qui n'a pas besoin d'une colonne IDENTITY est une simple table de jonction, comme le mappage d'un utilisateur à ses autorisations).

Je m'oppose à les ajouter aveuglément à chaque table dans un article de blog de 2010:

Mais les clés de substitution ont des cas d'utilisation valides - faites juste attention à ne pas supposer qu'elles garantissent l'unicité (c'est pourquoi elles sont parfois ajoutées - elles ne devraient pas être le seul moyen d'identifier de manière unique une ligne). Si vous devez utiliser un cadre ORM et que votre cadre ORM nécessite des clés entières à colonne unique même dans les cas où votre clé réelle n'est pas un entier, ou pas une seule colonne, ou aucune, assurez-vous de définir des contraintes / index uniques pour vos vraies clés aussi.

Aaron Bertrand
la source
Merci pour la réponse rapide. Oui, l'application utilise un ORM (EF). Il ne nécessite pas de clés de colonne à un seul entier, mais j'ai introduit cette restriction pour rendre certaines opérations génériques beaucoup plus faciles (au niveau de la conception). De plus, tous les caches d'application stockent tout dans les cartes (dictionnaires) pour une récupération rapide par clé et la clé doit être unique. Depuis, j'ai choisi des ints plutôt que des guids, je suis obligé d'utiliser IDENTITY pour n'importe quelle table dans laquelle j'insère. Pour les tables à valeurs fixes, IDENTITY n'est pas requis.
Alexei
Je pense qu'il existe des cas qui appellent à éviter la vérification de l'unicité des clés naturelles. En tant que personne qui travaille avec des données SIG, celle qui vient immédiatement à l'esprit est celle où la clé naturelle est soit juste la géométrie elle-même, soit la géométrie plus une clé étrangère. La recherche de choses selon une géométrie exacte sera toujours impraticable, il est donc peu probable qu'une contrainte d'unicité y soit très utile et puisse présenter des inconvénients en termes de performances. La même chose pourrait être vraie si une partie de la clé naturelle est une longue colonne de texte. Mais je suis d'accord: chaque fois que c'est possible, oui, une contrainte unique sur la clé naturelle doit être appliquée.
jpmc26
13

D'après mon expérience, la raison principale et écrasante d'utiliser un ID distinct pour chaque table est la suivante:

Dans presque tous les cas, mon client a prêté serment de sang lors de la phase de conception selon laquelle certains champs "naturels" externes XYZBLARGH_IDresteront uniques pour toujours et ne changeront jamais pour une entité donnée, et ne seront jamais réutilisés. Les propriétés de la clé primaire ont été rompues. Cela ne fonctionne tout simplement pas de cette façon.

Ensuite, d'un point de vue DBA, les éléments qui ralentissent ou gonflent une base de données ne sont certainement pas 4 octets (ou autre) par ligne, mais des éléments comme des index incorrects ou manquants, des réorganisations de table / index oubliées, de mauvais paramètres de réglage de RAM / espace de table , en négligeant d'utiliser des variables de liaison, etc. Ceux-ci peuvent ralentir la DB par des facteurs de 10, 100, 10000 ... pas une colonne ID supplémentaire.

Donc, même s'il y avait un inconvénient technique et mesurable à avoir 32 bits supplémentaires par ligne, il ne s'agit pas de savoir si vous pouvez optimiser l'ID, mais si l'ID sera essentiel à un moment donné, ce qui sera plus probable qu'improbable. Et je ne vais pas compter tous les avantages "logiciels" d'une position de développement logiciel (comme votre exemple ORM, ou le fait que cela facilite la tâche des développeurs de logiciels lorsque tous les ID par conception ont le même type de données, etc.) .

NB: notez que vous n'avez pas besoin d'un ID séparé pour n:mles tables d'association car pour ces tables les ID des entités associées doivent former une clé primaire. Un contre-exemple serait une n:massociation bizarre qui permet de multiples associations entre les deux mêmes entités pour une raison bizarre - celles-ci auraient alors besoin de leur propre colonne ID pour créer un PK. Il existe cependant des bibliothèques ORM qui ne peuvent pas gérer les PK multi-colonnes, ce qui serait une raison d'être indulgent avec les développeurs, s'ils doivent travailler avec une telle bibliothèque.

AnoE
la source
2
"association bizarre n: m qui permet de multiples associations entre les deux mêmes entités" TRÈS commun dans la vie réelle. Par exemple, une personne possède une voiture, puis les exigences changent pour être enregistrées lorsque la propriété a commencé et s'est terminée (une personne peut vendre une voiture et la racheter plus tard, et planter votre logiciel ....)
Ian Ringrose
Ouais, quelque chose comme ça, @IanRingrose.
AnoE
6

Si vous ajoutez invariablement une colonne supplémentaire vide de sens à chaque table et référencez uniquement ces colonnes en tant que clés étrangères, vous rendrez presque inévitablement la base de données plus complexe et difficile à utiliser. En effet, vous supprimerez les données d'intérêt pour les utilisateurs des attributs de clé étrangère et forcerez l'utilisateur / l'application à faire une jointure supplémentaire pour récupérer ces mêmes informations. Les requêtes deviennent plus complexes, le travail de l'optimiseur devient plus difficile et les performances peuvent en souffrir.

Vos tables seront plus peu peuplées de données "réelles" qu'elles ne l'auraient été autrement. La base de données sera donc plus difficile à comprendre et à vérifier. Vous pouvez également trouver difficile ou impossible d'appliquer certaines contraintes utiles (où les contraintes impliqueraient plusieurs attributs qui ne sont plus dans la même table).

Je vous suggère de choisir vos clés plus soigneusement et de les rendre entières uniquement si / quand vous avez de bonnes raisons de le faire. Basez vos conceptions de base de données sur une bonne analyse, l'intégrité des données, l'aspect pratique et des résultats vérifiables plutôt que de vous fier à des règles dogmatiques.

nvogel
la source
1
Et pourtant, de nombreux systèmes ont des clés primaires entières synthétiques sur chaque table (presque toutes les applications Ruby on Rails jamais écrites, par exemple), sans souffrir de tels problèmes. Ils ne souffrent pas non plus du problème de devoir pousser les modifications des clés primaires (qui ne devaient jamais se produire) à toutes les tables de clés étrangères.
David Aldridge
2
La question demandait d'éventuels inconvénients, d'où ma réponse. Je ne nie pas que les clés de substitution peuvent avoir du sens si elles sont utilisées à bon escient. Mais j'ai vu des tables avec 3,4,5 (ou beaucoup plus) de clés étrangères dénuées de sens qui nécessitaient donc 3,4,5 ou plus de jointures pour obtenir des résultats utiles. Une conception plus pragmatique aurait pu ne nécessiter aucune jointure.
nvogel
1
Je ne suis pas convaincu que c'est l'exécution de telles requêtes qui est le principal problème que les gens ont avec une telle conception - c'est l'écriture de la requête à laquelle ils s'opposent souvent.
David Aldridge
5

D'après mon expérience avec diverses bases de données, une clé primaire Integer est toujours meilleure que les applications qui n'ont aucune clé définie du tout. Ou qui ont des clés qui rejoignent une demi-douzaine de colonnes varchar de manière maladroite qui ne sont pas logiques ... (soupir)

J'ai vu des applications qui sont passées de PK entiers à des GUID. La raison en était que, dans certains cas, il était nécessaire de fusionner les données de plusieurs bases de données sources. Les développeurs ont changé toutes les clés pour les GUID afin que les fusions puissent se produire sans crainte de collisions de données, même sur des tables qui ne faisaient pas partie de la fusion (juste au cas où ces tables feraient jamais partie d'une future fusion).

Je dirais qu'un PK entier ne va pas vous mordre à moins que vous ne prévoyiez de fusionner des données provenant de sources distinctes ou que vous ayez des données qui dépassent vos limites de taille entière - tout cela est amusant et amusant jusqu'à ce que vous manquiez d'espace pour les insertions .

Je dirai cependant qu'il peut être judicieux de définir votre index cluster sur une colonne autre que votre PK, si la table est interrogée plus fréquemment de cette façon. Mais c'est un cas inhabituel, surtout si la majeure partie des mises à jour et des sélections sont basées sur les valeurs PK.

Came
la source
2
Cela ressemble à une terrible justification pour changer toutes les clés des guides. Je travaille actuellement avec une base de données qui utilise des guides pour toutes les clés de substitution .. ce n'est pas amusant.
Andy
2
Non. L'utilisation des GUID n'est pas amusante. Je ne les aime pas, mais je respecte leur valeur dans certains cas d'utilisation.
CaM
2

En mettant de côté:

  • Les guerres de religion (Google substitut vs clé naturelle)
  • La question distincte des index cluster à définir sur vos tables
  • La viabilité de la mise en cache de toutes vos données

À condition que vous utilisiez la suppression / mise à jour en masse le cas échéant et que vous ayez des index pour prendre en charge de telles opérations, je ne pense pas que vous rencontrerez des problèmes en raison de la norme PK que vous utilisez.
Il est possible que si vous demandez à EF de générer des requêtes avec des jointures, etc., elles ne soient pas aussi efficaces qu'elles le seraient avec un référentiel basé sur des clés naturelles, mais je ne connais pas suffisamment ce domaine pour dire avec certitude dans les deux cas.

TH
la source
4
Je ne peux pas penser à un seul cas où une jointure sur une clé naturelle serait plus efficace qu'une jointure sur un entier - pas beaucoup de clés naturelles peuvent être inférieures à 4 octets, et si elles le sont, il ne peut pas y avoir assez d'unique lignes pour faire la différence.
Aaron Bertrand
Pour un SQL compétent et optimisable, je suis d'accord, mais je faisais référence aux limitations possibles des générateurs SQL. Ma seule expérience dans ce domaine est demandée pour créer des vues étendues avec lesquelles EF pourrait être nourri à la cuillère - bien qu'il soit possible que les développeurs .net ne connaissent pas assez EF, ou qu'il y ait d'autres raisons.
TH
@AaronBertrand Je dirais que la seule façon dont ils pourraient être plus efficaces est si une jointure n'était pas du tout nécessaire. Le seul endroit où je considère l'utilisation de clés naturelles est avec des listes de codes standard tels que les codes de devise ISO4127 (qui sont reconnaissables par l'homme), et je pourrais utiliser GBP, EUR, etc. comme clé étrangère vers une clé primaire ou alternative sur le code de devise table.
David Aldridge
@David Bien sûr, je parlais de cas où des jointures sont nécessaires. Il y a beaucoup de cas où je ne veux pas que la clé naturelle prolifère dans toutes les tables associées, car les clés naturelles peuvent changer, et c'est pénible.
Aaron Bertrand
Hmmm, je vois comment ma réponse pourrait être mal comprise pour promouvoir des clés étrangères naturelles plutôt que des substituts. Pour être clair, je ne les ai mentionnés que parce que a) j'ai lu la question d'Alexei comme "est-ce un problème que nous n'utilisons pas de clés naturelles?", B) la question récapitulative d'Alexei a commencé par "du point de vue d'un DBA" et j'ai senti que je devrais en quelque sorte reconnaître qu'il y a plus d'une perspective et c) parce que je pense que les fonctionnalités ORM à utiliser dictent largement le choix (si cela peut réellement faire une différence). Je suis moi-même fermement dans le camp de la clé étrangère de substitution.
TH
2

Vous avez quelques facteurs pour vous guider,

  1. Définition et spéc.

    Si quelque chose est défini comme unique par la tâche ou les lois de la physique, vous perdez votre temps avec une clé de substitution.

  2. Unicité.

    Pour la raison personnelle, les jointures et les fonctionnalités de base de données de niveau supérieur, vous aurez besoin de: (a) colonne unique, (b) série unique de colonnes

    Tous les schémas suffisamment normalisés (1NF) fournissent l'un des éléments suivants. Si elles ne vous pas devez toujours créer un. Si vous avez une liste de personnes prévues pour faire du bénévolat dimanche, et cela comprend le nom et le prénom, vous voudrez savoir quand vous avez deux Joe Bobs.

  3. Implémentation et optimisation.

    Un int a tendance à être un petit formulaire de données qui est rapide pour la comparaison et l'égalité. Comparez cela avec une chaîne Unicode dont le classement peut dépendre des paramètres régionaux (emplacement et langue). Le stockage d'un 4242 dans une chaîne ASCII / UTF8 fait 4 octets. En le stockant sous forme d'entier, il tient dans 2 octets.

Donc, en ce qui concerne les inconvénients, vous avez quelques facteurs.

  1. Confusion et ambiguïté.

    1. L'entrée au blog @Aaron Bertrand résume bien cela. Il n'est pas auto-documenté d'avoir un OrderID par la spécification et la tâche, puis d'imposer un " OrderID " via l'implémentation de la base de données. Parfois, vous devez clarifier cela ou créer une convention, mais cela risque d’ajouter de la confusion.
  2. Espace.

    Les entiers ajoutent toujours de l'espace à la ligne. Et, si vous ne les utilisez pas, cela ne sert à rien.

  3. Regroupement.

    Vous ne pouvez commander vos données que dans un sens. Si vous imposez une clé de substitution qui n'est pas nécessaire, effectuez-vous un cluster de cette façon ou de la manière de la clé naturelle?

Evan Carroll
la source
Avantages et inconvénients agréables et courts.
Alexei
@Alexei merci, pensez à le marquer comme choisi s'il correspond à ce que vous recherchez. Ou, demandant des éclaircissements.
Evan Carroll