Est-ce une bonne pratique de toujours avoir une clé primaire entière auto-incrémentée?

191

Dans mes bases de données, j'ai tendance à prendre l'habitude de disposer d'une clé primaire entière auto-incrémentée avec le nom idde chaque table que je crée, de sorte que j'ai une recherche unique pour une ligne particulière.

Est-ce considéré comme une mauvaise idée? Y at-il des inconvénients à le faire de cette façon? Parfois, j'aurai plusieurs index, comme id, profile_id, subscriptionsidest l'identifiant unique, des profile_idliens vers l'étranger idd'une Profiletable, etc.

Ou existe-t-il des scénarios dans lesquels vous ne souhaitez pas ajouter un tel champ?

AJJ
la source
61
Examinons le problème des chars allemands pour un exemple où un identificateur simple à incrémentation automatique pose problème. Bien sûr, cela ne compte que si vous utilisez vos identifiants en public.
Bergi
24
@ArukaJ Le fait est que des informations sur le système sont divulguées. Par exemple, supposons que la base de données contienne des publications écrites par l'utilisateur, chacune d'elles obtenant un identifiant séquentiel. Supposons que vous réalisiez quatre messages dont chacun porte un identifiant: à 4 heures du matin (20), 5 heures du matin (25), 20 heures (100) et à 21 heures (200). En regardant les identifiants, vous pouvez voir que seulement 5 messages ont été ajoutés entre 4h et 5h du matin, alors que 100 ont été ajoutés entre 20h et 21h. Si vous tentiez de choisir l'heure d'une attaque par déni de service, cela pourrait être une information précieuse.
Joshua Taylor
29
Pour tout le monde se plaindre du "problème des chars allemands" ... si la seule chose qui empêche une personne d'accéder aux données qu'elle ne devrait pas, c'est une clé de votre URL ... vous avez des problèmes plus importants que le GUID ou Auto INT.
Matthew Whited
11
@ MatthewWhited Il ne s'agit pas uniquement d'échanger des paramètres dans une URL. Supposons que vous utilisiez un site et que vous créiez l’actif 100 à la fois tet l’actif 120 à la fois t + 60. Si vous pouvez voir ces deux identifiants (100 et 120) sous une forme non obscurcie, vous connaissez maintenant le nombre total d'actifs existants, ainsi que le taux approximatif de leur création. C'est une fuite d'informations. Ce n'est pas purement hypothétique.
Chris Hayes
15
"Est-ce une bonne pratique de toujours ..." No.
brian_o

Réponses:

137

Ce n'est jamais une mauvaise idée d'avoir un identifiant de ligne unique garanti. Je suppose que je ne devrais pas dire jamais - mais allons-y avec l'écrasante majorité du temps, c'est une bonne idée.

Les inconvénients théoriques potentiels incluent un index supplémentaire pour maintenir et un espace de stockage supplémentaire utilisé. Cela n'a jamais été une raison suffisante pour moi de ne pas en utiliser.

Grand maître b
la source
11
C'est ce que je fais. La plupart des gens utilisent soit 'id', soit 'tablename_id' (comme user_id). L'argument n'est généralement pas nécessaire si la colonne est nécessaire, mais comment le nommer.
GrandmasterB
103
Personnellement, je pense que le nom de la table devrait impliquer le reste. TableName.idpar opposition à TableName.TableName_id, car à quoi d'autre idva-t-il faire référence? Si j'ai un autre champ d'identifiant dans la table, je le préfixerai par un nom de table s'il fait référence à une autre table
AJJ
10
@ArukaJ vous avez mentionné que vous utilisiez SQLite. C'est en fait un cas particulier, car cela crée toujours une telle colonne "sous le capot". Donc, vous n'utilisez même pas d'espace supplémentaire car vous en obtenez un que vous le vouliez ou non. De plus, le rowid de SQLite est toujours un entier de 64 bits. Si ma compréhension est correcte, si vous définissez une ligne auto-incrémentée, ce sera un alias pour le rowid interne. Alors tu pourrais bien faire toujours ça! Voir sqlite.org/autoinc.html
GrandmasterB
9
La seule exception à laquelle je peux penser est si vous avez un identifiant unique qui est généré d’une autre manière, auquel cas ce devrait être la clé primaire et un identifiant auto-incrémenté est redondant.
HamHamJ
4
@GrandmasterB: La version actuelle de SQLite permet de créer des WITHOUT ROWIDtables (avec un explicite PRIMARY KEY) sous forme d'optimisation. Mais sinon, une INTEGER PRIMARY KEYcolonne est un alias pour le rowid.
dan04
92

Je suis en désaccord avec toutes les réponses avant. Il y a de nombreuses raisons pour lesquelles c'est une mauvaise idée d'ajouter un champ d'incrémentation automatique dans toutes les tables.

Si vous avez une table où il n'y a pas de clés évidentes, un champ d'auto-incrémentation semble une bonne idée. Après tout, vous ne voulez pas select * from blog where body = '[10000 character string]'. Tu préfères select * from blog where id = 42. Je dirais que dans la plupart des cas, ce que vous voulez vraiment, c'est un identifiant unique; pas d'identifiant unique séquentiel. Vous voudrez probablement utiliser un identifiant unique universel.

Il existe des fonctions dans la plupart des bases de données pour générer des identifiants uniques aléatoires ( uuiddans mysql, postgres. newidDans mssql). Celles-ci vous permettent de générer des données dans plusieurs bases de données, sur différentes machines, à tout moment, sans connexion réseau, et de fusionner des données sans aucun conflit. Cela vous permet de configurer plus facilement plusieurs serveurs et même des centres de données, comme par exemple avec microservices.

Cela évite également aux attaquants de deviner les URL des pages auxquelles ils ne devraient pas avoir accès. S'il y en a, https://example.com/user/1263il y en a probablement https://example.com/user/1262aussi. Cela pourrait permettre l'automatisation d'un exploit de sécurité dans la page de profil de l'utilisateur.

Il y a aussi beaucoup de cas où une colonne d'uuid est inutile ou même nuisible. Disons que vous avez un réseau social. Il y a une userstable et une friendstable. La table friends contient deux colonnes d’ID utilisateur et un champ à incrémentation automatique. Vous voulez 3être amis avec 5, alors vous insérez 3,5dans la base de données. La base de données ajoute un identifiant et des magasins à incrémentation automatique 1,3,5. D'une manière ou d'une autre, l'utilisateur 3clique à nouveau sur le bouton "ajouter un ami". Vous insérez 3,5à nouveau dans la base de données, la base de données ajoute un identifiant d'incrémentation automatique et des insertions 2,3,5. Mais maintenant 3et 5sont amis deux fois! C'est un gaspillage d'espace, et si vous y réfléchissez, la colonne à incrémentation automatique l'est également. Tout ce dont vous avez besoin pour savoir si aetbsont amis est de sélectionner pour la ligne avec ces deux valeurs. Ils constituent ensemble un identifiant de ligne unique. (Vous voudrez probablement écrire une logique pour vous en assurer 3,5et vous 5,3dédupliquer.)

Il existe encore des cas où des identifiants séquentiels peuvent être utiles, par exemple lors de la construction d'un raccourcisseur d'URL, mais la plupart du temps (et même avec le raccourcisseur d'URL), un identifiant unique généré de manière aléatoire correspond à ce que vous voulez vraiment utiliser.

TL; DR: utilisez les UUID plutôt que l'incrémentation automatique, si vous ne disposez pas déjà d'un moyen unique d'identifier chaque ligne.

Filip Haglund
la source
26
Le problème avec les UUID est qu’ils prennent trop de place pour la majorité des tables. Utilisez le bon identifiant unique pour chaque table.
Stephen
49
Le paragraphe entier sur l'unicité est théorique - l'unicité peut être appliquée, avec ou sans clé primaire. De plus, les UUID sont meilleurs du point de vue théorique, mais ils sont terribles à utiliser pour déboguer / exécuter des tâches DBA ou pour faire tout ce qui ne résiste pas aux attaques.
11
Un autre scénario où les UUID sont meilleurs: implémenter une opération PUT idempotente, de sorte que vous puissiez réessayer en toute sécurité les demandes sans introduire de lignes en double.
yurez
21
Sur le point "deviner l'URL", avoir un identifiant unique (séquentiel ou autre) n'implique pas d'exposer cet identifiant aux utilisateurs de l'application.
Dave Sherohman
7
Purement du point de vue de la base de données, cette réponse est complètement fausse. L'utilisation d'UUID au lieu d'incrémentation automatique de nombres entiers augmente trop rapidement les index et affecte négativement les performances et la consommation de mémoire. Si vous parlez du point de vue du service Web ou de l'application Web, il devrait de toute façon y avoir une couche entre la base de données et le serveur frontal. Tout le reste est mauvais design. L'utilisation des données comme clé primaire est encore pire. Les clés primaires ne doivent être utilisées que sur la couche de données, nulle part ailleurs.
Drunken Code Monkey
60

Les clés automatiques ont généralement des avantages.

Mais certains inconvénients possibles pourraient être:

  • Si vous avez une clé métier, vous devez également ajouter un index unique sur cette ou ces colonne (s) afin de faire respecter les règles métier.
  • Lorsque vous transférez des données entre deux bases de données, en particulier lorsque celles-ci se trouvent dans plusieurs tables (c'est-à-dire maître / détail), ce n'est pas simple, car les séquences ne sont pas synchronisées entre les bases de données, et vous devrez d'abord créer une table d'équivalence à l'aide de la touche clé commerciale en tant que correspondance pour savoir quel ID de la base de données d'origine correspond à quel ID de la base de données cible. Cela ne devrait cependant pas poser de problème lors du transfert de données depuis / vers des tables isolées.
  • De nombreuses entreprises disposent d’outils de reporting ad-hoc, graphiques, de type pointer-cliquer, glisser-déposer. Étant donné que les identifiants auto-incrémentaux n'ont pas de sens, ce type d'utilisateurs aura du mal à donner un sens aux données en dehors de "l'application".
  • Si vous modifiez accidentellement la clé de gestion, il est fort probable que vous ne récupériez jamais cette ligne car vous ne pouvez plus rien identifier par les utilisateurs. Cela a causé une erreur dans la plate-forme BitCoin une fois .
  • Certains concepteurs ajoutent un ID à une table de jointure entre deux tables, alors que la PK doit simplement être composée des deux ID étrangers. Évidemment, si la table de jointure est composée d'au moins trois tables, un ID auto-incrémental est logique, mais vous devez ensuite ajouter une clé unique lorsqu'elle s'applique à la combinaison de FK pour appliquer des règles métier.

Voici une section de l'article de Wikipedia sur les inconvénients des clés de substitution.

Tulains Córdova
la source
13
Blâmer la faille mt.gox sur les clés de substitution semble plutôt douteux. Le problème était qu'ils incluaient tous les champs dans leur clé composée, même les champs mutables / malléables.
CodesInChaos
6
L’inconvénient "social" de l’utilisation de clés à incrémentation automatique est que, parfois, "l’entreprise" suppose qu’il ne doit jamais y avoir de lacunes et exige de savoir ce qui est arrivé aux lignes manquantes lorsqu’une insertion a échoué (annulation de transaction).
Rick Ryker
4
Un autre inconvénient est que si le système devient si volumineux que vous devez partager la base de données, vous ne pouvez plus utiliser l'auto-incrémentation pour produire une clé unique au monde. Lorsque vous en arriverez là, vous pourriez avoir beaucoup de code reposant sur cette hypothèse. Il existe d'autres moyens de générer un identifiant unique qui continuera à fonctionner si la base de données est partagée.
Kasperd
1
@Voo Il n'est pas garanti que la base de données choisie supporte cela. Et essayer de mettre en œuvre une couche plus haute que la base de données elle-même signifie que vous perdez certaines des garanties que SQL vous donnerait. Enfin, toute affectation centralisée d’ID augmentera la latence si vous avez un système distribué.
Kasperd
1
@Voo Bien entendu, quelle que soit l'échelle du système, il ne faut pas trop présumer de la nature des identifiants auto-incrémentés. Si vous n'avez qu'une seule base de données, elles sont affectées dans l'ordre, mais rien ne garantit qu'elles sont validées dans l'ordre. Et il peut y avoir un vide dans la séquence car toutes les transactions ne sont pas validées.
Kasperd
20

Juste pour être contraire, non, vous n'avez PAS toujours besoin d'un PK AutoInc numérique.

Si vous analysez soigneusement vos données, vous identifiez souvent des clés naturelles dans les données. C'est souvent le cas lorsque les données ont une signification intrinsèque pour l'entreprise. Parfois, les PK sont des artefacts d'anciens systèmes que les utilisateurs professionnels utilisent comme seconde langue pour décrire les attributs de leur système. J'ai vu des numéros d'identification de véhicule (VIN) utilisés comme clé principale d'un tableau "Véhicule" dans un système de gestion de flotte, par exemple.

Quelle que soit l'origine, si vous avez déjà un identifiant unique, utilisez-le. Ne créez pas une seconde clé primaire sans signification; c'est un gaspillage et peut causer des erreurs.

Parfois, vous pouvez utiliser un PK AutoInc pour générer une valeur client significative, par exemple des numéros de stratégie. Définir la valeur de départ sur quelque chose de sensé et appliquer les règles de gestion relatives aux zéros non significatifs, etc. Il s'agit probablement d'une approche du "meilleur des deux mondes".

Lorsque vous avez un petit nombre de valeurs relativement statiques, utilisez des valeurs qui ont du sens pour l'utilisateur du système. Pourquoi utiliser 1, 2, 3 alors que vous pouvez utiliser L, C, H, où L, H et C représentent Vie, Voiture et domicile dans un contexte "type de police" d'assurance ou, si vous revenez à l'exemple du VIN, que diriez-vous d'utiliser "TO "pour Toyota? Toutes les voitures Toyata ont un VIN qui commence par "TO". C’est une chose à retenir de la part des utilisateurs, qui risquent moins d’introduire des erreurs de programmation et des erreurs et qui peut même servir de substitut utilisable pour une description complète dans les rapports de gestion, ce qui simplifie les rapports. écrire et peut-être plus rapide à générer.

Un développement ultérieur de ceci est probablement "un pont trop loin" et je ne le recommande généralement pas, mais je l'inclue par souci d'exhaustivité et vous pourrez en trouver un bon usage. Autrement dit, utilisez la description comme clé primaire. Pour des données qui changent rapidement, c'est une abomination. Pour les données très statiques rapportées sur Tout le temps , peut-être pas. Il suffit de le mentionner pour que cela reste une possibilité.

J'utilise des PC AutoInc, je m'engage simplement dans mon cerveau et cherche d'abord de meilleures alternatives. L'art de la conception de base de données donne quelque chose de significatif qui peut être interrogé rapidement. Avoir trop de jointures empêche cela.

ÉDITER Un autre cas crucial où vous n'avez pas besoin d'une PK générée automatiquement est le cas des tables représentant l'intersection de deux autres tables. Pour rester fidèle à l’analogie voiture, une voiture a 0..n accessoires, chaque accessoire peut être trouvé sur de nombreuses voitures. Donc, pour représenter ceci, vous créez une table Car_Accessory contenant les PK de Car et Accessory et d'autres informations pertinentes sur le lien Dates, etc.

Ce dont vous n’avez pas besoin (généralement), c’est un AutoInc PK sur cette table - il ne sera accessible que par la voiture "dites-moi quels accessoires sont sur cette voiture" ou à partir de l’accessoire "dites-leur quelles voitures ont cet accessoire"

mcottle
la source
4
> Toutes les voitures Toyata ont un VIN qui commence par "TO", ce n'est tout simplement pas vrai. Ils commencent par "JT" s'ils sont fabriqués au Japon. Les Toyota construites en Amérique ont des NIV complètement différents fr.wikibooks.org/wiki/…
Monty Harder
17
Don't create a second, meaningless primary key; it's wasteful and may cause errors.Cependant, si vous établissez l'unicité d'un enregistrement en combinant 6 colonnes, le fait de les relier toutes les 6 tout le temps est sujet à l'erreur. Les données ont naturellement une PK, mais il vaut mieux utiliser une idcolonne et une contrainte unique sur ces 6 colonnes.
Brad
14
J'admets que certaines de ces suggestions vont un peu loin pour moi. Oui, être pragmatique, c'est bien, mais je ne peux pas compter combien de fois quelqu'un a juré la vie de son premier-né, ce que certains attribuent en dehors du domaine restera unique pour le reste des jours. En général, cela a bien fonctionné jusqu'à la deuxième semaine après la mise en production, lorsque les premiers doublons sont arrivés. ;) L'utilisation d'une "description" en tant que PC est tout à fait dépassée.
AnoE
2
@Monty, ma mauvaise, tu as raison. Mémoire faillible, cela fait 20 ans que j'ai conçu les systèmes de gestion de flotte. Non, le VIN n'était pas la clé primaire :) J'ai utilisé un IIRC AutoInc Asset_ID qui mène à quelque chose que j'ai oublié. Tables qui sont des lieurs pour des relations multiples, par exemple voiture à accessoire (par exemple, le toit ouvrant) De nombreuses voitures ont de nombreux accessoires; vous avez donc besoin d'une table "Car_Accessory" qui contient Car_ID et Accessory_ID mais n'a absolument PAS besoin de Car_Accesory_ID une PK AutoInc.
Mcottle
7
Il est vraiment étonnant de constater le peu de "clés naturelles" véritablement immuables. Le SSN? Non, ils peuvent changer. C'est rare, mais cela peut arriver. Noms d'utilisateur? Nan. Finalement, quelqu'un aura une raison valable de changer. Le NIV est souvent un exemple classique, mais il n'y en a pas beaucoup d'autres. Même les adresses personnelles peuvent changer, en fonction des changements de noms de rue.
Erik Funkenbusch
12

De nombreuses tables ont déjà un identifiant unique et naturel. N'ajoutez pas une autre colonne d'identifiant unique (incrémentation automatique ou autre) sur ces tables. Utilisez plutôt l'identifiant unique naturel. Si vous ajoutez un autre identifiant unique, vous avez essentiellement une redondance (duplication ou dépendance) dans vos données. Cela va à l’encontre des principes de normalisation. Un identifiant unique dépend de l'autre pour la précision. Cela signifie qu'ils doivent être parfaitement synchronisés en tout temps sur tous les systèmes gérant ces lignes. C’est une autre fragilité de l’intégrité de vos données que vous ne voulez pas vraiment gérer et valider à long terme.

De nos jours, la plupart des tables n'ont pas vraiment besoin de l'amélioration mineure des performances qu'une colonne id unique supplémentaire donnerait (et parfois même, cela nuit à la performance). En règle générale, en informatique, évitez les licenciements comme la peste! Résistez-y partout où cela vous est suggéré. C'est un anathème. Et prenez en compte la citation. Tout devrait être aussi simple que possible, mais pas plus simple. Ne pas avoir deux identifiants uniques où un suffira, même si le naturel semble moins bien rangé.

Brad Thomas
la source
3
Ne devriez-vous pas utiliser des identifiants «naturels» comme clés primaires s'il est absolument garanti qu'ils ne changeront jamais? Par exemple, vous ne devez pas utiliser le numéro de permis de conduire comme clé primaire, car si une personne obtient un nouveau permis de conduire, vous devrez mettre à jour non seulement cette table, mais également toutes les tables avec des clés étrangères qui les référencent!
ekolis
1
Il y a plusieurs raisons pour lesquelles le numéro de permis de conduire ne peut être considéré comme un identifiant unique et naturel. Tout d'abord, certaines d'entre elles sont dérivées d'autres données, telles que la date de naissance et le nom. Ils ne sont pas garantis uniques à travers les états. Et pour prendre votre exemple, lorsqu'une personne se voit attribuer une nouvelle licence avec le même numéro, mais peut-être avec une expiration prolongée, que se passe-t-il ensuite? Ils ont une licence différente avec le même numéro. Un identifiant naturel doit toujours remplir les propriétés de base d'une clé primaire. Le numéro de permis de conduire (du moins aux États-Unis) présente quelques lacunes à cet égard.
Brad Thomas
1
OK, je suppose que j’ai mal compris la définition de l’identité naturelle; Je pensais qu'il s'agissait simplement d'un identifiant défini par les règles de gestion, qu'il soit ou non garanti qu'il soit immuable.
ekolis
10

Sur les systèmes plus importants, ID est un accélérateur de cohérence, utilisez-le presque partout. Dans ce contexte, les clés primaires individuelles ne sont PAS recommandées, elles sont coûteuses en bout de ligne (lisez pourquoi).

Chaque règle comporte une exception. Par conséquent, vous n'avez peut-être pas besoin d'un ID d'auto-incrément entier sur les tables de transfert utilisées pour l'exportation / importation et sur les tables unidirectionnelles ou temporaires similaires. Vous préféreriez également un GUID plutôt qu'un ID sur des systèmes distribués.

De nombreuses réponses suggèrent de prendre la clé unique existante. Bien même si elle a 150 caractères? Je ne pense pas.

Maintenant, mon point principal:

Il semble que les adversaires de l'ID d'auto-incrémentation parlent de petites bases de données contenant jusqu'à 20 tables. Là, ils peuvent se permettre une approche individuelle à chaque table.

MAIS une fois que vous avez un ERP avec plus de 400 tables, avoir un ID d’auto-incrémentation entier n’importe où (sauf dans les cas mentionnés ci-dessus) a tout son sens. Vous ne comptez pas sur d'autres champs uniques, même s'ils sont présents et sécurisés pour leur caractère unique.

  • Vous bénéficiez de conventions universelles permettant de gagner du temps, de gagner du temps et de ne pas oublier.
  • Dans la plupart des cas, vos JOINtables, sans avoir besoin de vérifier quelles sont les clés.
  • Vous pouvez avoir des routines de code universelles travaillant avec votre colonne d'auto-incrémentation d'entier.
  • Vous pouvez étendre votre système avec de nouvelles tables ou des plugins utilisateur non prévus auparavant en vous référant simplement aux ID des tables existantes. Ils sont déjà là depuis le début, aucun frais supplémentaire pour les ajouter.

Sur les systèmes plus grands, il peut être utile d'ignorer les avantages mineurs de ces clés primaires individuelles et d'utiliser systématiquement un ID d'auto-incrémentation entier dans la plupart des cas. L'utilisation de champs uniques existants en tant que clés primaires permet peut-être de gagner quelques octets par enregistrement, mais un temps de stockage ou d'indexation supplémentaire ne pose aucun problème pour les moteurs de base de données actuels. En fait, vous perdez beaucoup plus d’argent et de ressources en raison du temps perdu par les développeurs / mainteneurs. Les logiciels actuels doivent être optimisés pour le temps et les efforts des programmeurs - quelle approche avec des ID cohérents donne de meilleurs résultats.

Miroxlav
la source
De par mon expérience personnelle, je suis tout à fait d’accord avec la seconde partie de votre réponse. Vous aurez besoin de beaucoup de clés uniques au monde, beaucoup moins souvent que d'index rapides et compacts. Si vous en avez besoin, créez une table GlobalEntities avec un ID généré automatiquement et une colonne UUID. Ajoutez ensuite une clé étrangère ExGlobalEntityId à la table Customers, par exemple. Ou utilisez un hachage de certaines des valeurs.
Drunken Code Monkey
8

Ce n'est pas une bonne pratique de dessins superflus. C'est-à-dire que ce n'est pas une bonne pratique d'avoir toujours une clé primaire incrémentée automatiquement quand on n'en a pas besoin.

Voyons un exemple où un n'est pas nécessaire.

Vous avez une table pour les articles. Elle contient une clé primaire int idet une colonne varchar nommée title.

Vous avez également un tableau complet de catégories d'articles - idclé primaire int, varchar name.

Une ligne dans la table des articles a un idde 5 et un title "Comment faire cuire une oie avec du beurre". Vous souhaitez lier cet article aux lignes suivantes de votre table Catégories: "Volaille" ( id : 20), "Oie" ( id : 12), "Cuisson" ( id : 2), "Beurre" (id: 9) .

Maintenant, vous avez 2 tableaux: articles et catégories. Comment créez-vous la relation entre les deux?

Vous pouvez avoir une table avec 3 colonnes: id (clé primaire), article_id (clé étrangère), category_id (clé étrangère). Mais maintenant vous avez quelque chose comme:

| id | a_id | c_id |
| 1 | 5 | 20 |
| 2 | 5 | 12 |
| 3 | 5 | 2 |

Une meilleure solution consiste à avoir une clé primaire composée de 2 colonnes.

| a_id | c_id |
| 5 | 20 |
| 5 | 12 |
| 5 | 2 |

Ceci peut être accompli en faisant:

create table articles_categories (
  article_id bigint,
  category_id bigint,
  primary key (article_id, category_id)
) engine=InnoDB;

Une autre raison de ne pas utiliser un entier à incrémentation automatique est si vous utilisez des UUID pour votre clé primaire.

Par leur définition, les UUID sont uniques, ce qui revient au même résultat que l'utilisation d'entiers uniques. Ils ont également leurs propres avantages (et inconvénients) par rapport aux nombres entiers. Par exemple, avec un UUID, vous savez que la chaîne unique à laquelle vous faites référence pointe vers un enregistrement de données particulier; Ceci est utile dans les cas où vous ne possédez pas une base de données centrale ou lorsque les applications ont la possibilité de créer des enregistrements de données hors connexion (puis les télécharger ultérieurement dans la base de données).

En fin de compte, vous devez ne pas penser aux clés primaires. Vous devez penser à eux comme à la fonction qu'ils remplissent. Pourquoi avez-vous besoin de clés primaires? Pouvoir identifier de manière unique des ensembles de données spécifiques d'une table à l'aide d'un champ qui ne sera pas modifié à l'avenir. Avez-vous besoin d'une colonne particulière appelée idà cet effet ou pouvez-vous baser cette identification unique sur d'autres données (immuables)?

anw
la source
7

Ou existe-t-il des scénarios dans lesquels vous ne souhaitez pas ajouter un tel champ?

Sûr.

Tout d’abord, il existe des bases de données qui n’ont pas d’auto-incrémentation (par exemple, Oracle, qui n’est certainement pas l’un des plus petits candidats au monde). Cela devrait être une première indication que tout le monde ne les aime ou n'a pas besoin d'eux.

Plus important encore , pensez à ce que l'ID fait est - il est une clé primaire pour vos données. Si vous avez une table avec une clé primaire différente, vous n'avez pas besoin d'un ID et vous ne devriez pas en avoir un. Par exemple, une table (EMPLOYEE_ID, TEAM_ID)(où chaque employé peut appartenir à plusieurs équipes simultanément) a une clé primaire clairement définie composée de ces deux ID. L'ajout d'une IDcolonne auto-incrémentée , qui est également une clé primaire pour cette table, n'aurait aucun sens. Maintenant, vous traînez avec 2 clés primaires, et le premier mot de "clé primaire" devrait vous donner l’allusion que vous ne devriez en posséder qu’une.

AnoE
la source
9
(Pas un utilisateur Oracle alors pardonnez la question mais) Oracle n'utilise-t-il pas Sequence de la même façon que d'autres utilisent Autoincrement / Identity? Est-ce que dire qu'Oracle n'a pas de type de données Autoincrement est juste un argument sématique?
Brad
Eh bien, c'était juste un petit point; l'essentiel est qu'un identifiant en cours d'exécution ne convient pas à toutes les tables. Par conséquent, s'habituer à claquer un identifiant automatique sur chaque table risque de ne pas être le plus sage.
AnoE
il n'y a pas deux clés primaires, il n'y a qu'une seule clé primaire et tous les autres sont appelés clés candidats s'ils peuvent aussi servir de clés primaires.
rahul tyagi
7

J'utilise généralement une colonne "identité" (entier auto-incrémenté) lors de la définition de nouvelles tables pour des données "à vie longue" (enregistrements que je m'attends à insérer une fois et à conserver indéfiniment même s'ils sont "supprimés logiquement" en définissant un champ de bits ).

Si vous ne souhaitez pas les utiliser, il y a quelques situations dans lesquelles je peux penser, la plupart d'entre elles se résumant à des scénarios dans lesquels une table sur une instance de la base de données ne peut pas être la source faisant autorité pour les nouvelles valeurs d'ID:

  • Lorsque les identifiants incrémentiels seraient trop d'informations pour un attaquant potentiel. L'utilisation d'une colonne d'identité pour les services de données "destinés au public" vous rend vulnérable au "problème des chars allemands"; Si l’enregistrement ID 10234 existe, cela signifie que l’enregistrement 10233, 10232, etc. existe, retourne au moins à l’enregistrement 10001, puis il est facile de rechercher les enregistrements 1001, 101 et 1 pour déterminer où votre colonne d’identité a commencé. Les GUID V4 composés principalement de données aléatoires modifient ce comportement incrémentiel, de sorte que, du fait qu’un GUID existe, un GUID créé en incrémentant ou décrémentant un octet du GUID n’existant pas nécessairement, il est donc plus difficile pour un attaquant d’utiliser un service indtended pour la récupération d'un enregistrement unique en tant qu'outil de vidage. Il existe d'autres mesures de sécurité qui peuvent mieux limiter l'accès, mais cela aide.
  • Dans M: M, tables de références croisées. C'est un peu comme ça, mais je l'ai déjà vu auparavant. Si vous avez une relation plusieurs à plusieurs entre deux tables de votre base de données, la solution à utiliser est une table de références croisées contenant des colonnes de clé étrangère faisant référence à la PK de chaque table. La PK de cette table devrait pratiquement toujours être une clé composée des deux clés étrangères, pour obtenir le comportement d'index intégré et pour garantir l'unicité des références.
  • Lorsque vous envisagez d’insérer et de supprimer en bloc sur cette table beaucoup de choses. L'inconvénient majeur des colonnes d'identité est probablement le hoopla supplémentaire auquel vous devez faire face lors de l'insertion de lignes d'une autre table ou requête, dans lesquelles vous souhaitez conserver les valeurs de clé de la table d'origine. Vous devez activer "insert d’identité" (quelle que soit la méthode utilisée dans votre SGBD), puis vous assurer manuellement que les clés que vous insérez sont uniques, puis lorsque vous avez terminé l’importation, vous devez définir le compteur d’identité dans les métadonnées de la table à la valeur maximale présente. Si cette opération se produit souvent sur cette table, envisagez un schéma PK différent.
  • Pour les tables distribuées.Les colonnes d'identité conviennent parfaitement aux bases de données à instance unique, aux paires de basculement et à d'autres scénarios dans lesquels une instance de base de données est la seule autorité sur l'ensemble du schéma de données à un moment donné. Cependant, il n’ya que si gros que vous pouvez y aller et qu’un ordinateur reste suffisamment rapide. La réplication ou l'envoi du journal des transactions peut vous fournir des copies supplémentaires en lecture seule, mais il existe également une limite à l'échelle de cette solution. Tôt ou tard, vous aurez besoin de deux ou plusieurs instances de serveur qui gèrent les insertions de données, puis se synchronisent les unes avec les autres. Lorsque cette situation se produit, vous voudrez un champ GUID au lieu d'un champ incrémentiel, car la plupart des SGBD sont préconfigurés pour utiliser une partie des GUID générés en tant qu'identificateur spécifique à l'instance, puis générer le reste de l'identifiant de manière aléatoire. ou progressivement. Dans tous les cas,
  • Lorsque vous devez imposer l'unicité de plusieurs tables de la base de données.Par exemple, dans les systèmes de comptabilité, par exemple, vous gérez le grand livre général (avec une ligne pour chaque crédit ou débit de chaque compte déjà généré, ce qui le rend très volumineux très rapidement) sous la forme d'une séquence de tableaux représentant chacun un mois calendaire / année. Des vues peuvent ensuite être créées pour les lier ensemble pour la création de rapports. Logiquement, c'est une très grande table, mais sa découpe facilite les tâches de maintenance de la base de données. Cependant, cela pose le problème de la gestion des insertions dans plusieurs tables (vous permettant de commencer à enregistrer les transactions le mois prochain tout en fermant la dernière) sans se retrouver avec des clés en double. Encore une fois, les GUID au lieu des colonnes d’identité d’identité constituent la solution idéale, car le SGBD est conçu pour les générer de manière vraiment unique.

Il existe des solutions de contournement qui permettent l'utilisation de colonnes d'identité dans ces situations, comme je l'ai mentionné avec espoir, mais dans la plupart d'entre elles, la mise à niveau de la colonne d'entité d'identité vers un GUID est plus simple et résout le problème plus complètement.

KeithS
la source
1
Dans certains cas, vous pouvez toujours avoir besoin d'un ID dans les tables M: N (à l'aide de colonnes ID, ID_M, ID_N) en raison de l'attachement de propriétés aux instances de votre relation M: N.
miroxlav
Il n’est pas garanti que la V4 GUIDS utilise un fichier PNRG très fort sur le plan cryptographique, vous ne devriez donc pas vous en fier à votre premier exemple imo (bien que si votre moteur de base de données vous promet des promesses plus fortes, vous pourriez vous en sortir, mais c’est plutôt non portable). Sinon, un post bien motivé.
Voo
1
@miroxlav - J'affirmerais que si une table contient suffisamment de métadonnées supplémentaires concernant la relation, il est judicieux de définir une PC distincte en dehors des deux FK, il ne s'agit plus vraiment d'une table de références croisées; c'est sa propre entité qui fait référence aux deux autres.
KeithS
@Voo - Vous avez raison, il n'est pas garanti que les GUID V4 soient cryptographiquement aléatoires, ils sont tout simplement uniques (comme tous les GUID). Cependant, les numéros de queue des chasseurs à réaction américains ne sont pas générés non plus à partir d'algorithmes ou de données de départ cryptographiquement aléatoires. Ce que vous recherchez réellement est un domaine peu peuplé. un GUID V4 contient 112 octets de données aléatoires, capables d’identifier de manière unique 5 enregistrements 33e.
KeithS
Pour mettre ce chiffre en perspective, chaque homme, femme et enfant de la planète (sur les 7 milliards) pourrait disposer de 741 trillions de points de données catalogués et identifiés individuellement dans notre base de données, et nous n'utiliserions toujours qu'une seule valeur GUID par milliard disponible. Le Big Data, en tant que secteur mondial, n’est même pas proche de cette échelle de connaissances. Même si un motif est attribué à la génération du GUID, il existe d'autres sources d'entropie impliquées, telles que l'ordre dans lequel les données entrent dans le système et se voient attribuer un GUID.
KeithS
7

Une clé primaire auto-incrémentée (identité) est une bonne idée, sauf pour noter qu'elle n'a pas de sens en dehors du contexte de la base de données et des clients immédiats de cette base de données. Par exemple, si vous transférez et stockez certaines des données dans une autre base de données, puis écrivez des données différentes dans les deux tables de la base de données, les identifiants divergent. En d'autres termes, les données dont l'identifiant est 42 dans une base de données ne correspondront pas nécessairement aux données. avec un identifiant de 42 dans l'autre.

Compte tenu de cela, s'il est nécessaire de pouvoir identifier les lignes de manière unique en dehors de la base de données (ce qui est souvent le cas), vous devez disposer d'une clé différente à cet effet. Une clé d'entreprise soigneusement sélectionnée fera l'affaire, mais vous vous retrouverez souvent dans un nombre important de colonnes afin de garantir l'unicité. Une autre technique consiste à utiliser une colonne Id en tant que clé primaire en cluster à incrémentation automatique et une autre colonne à identificateur unique (guid) en tant que clé unique non en cluster, dans le but d’identifier de manière unique la ligne, où qu’elle se trouve dans le monde. La raison pour laquelle vous avez toujours une clé auto-incrémentée dans ce cas est qu'il est plus efficace de mettre en cluster et d'indexer la clé auto-incrémentée que de faire la même chose avec un guid.

Dans le cas où vous ne souhaiteriez peut-être pas une clé à incrémentation automatique, il s’agirait d’une table plusieurs-à-plusieurs où la clé primaire est composée des colonnes Id de deux autres tables (vous pouvez toujours avoir une clé à incrémentation automatique ne vois pas le but).

Une autre question concerne le type de données de la clé auto-incrémentée. L'utilisation d'un Int32 vous donne une large plage de valeurs, mais relativement limitée. Personnellement, j’utilise fréquemment les colonnes bigint pour l’Id, afin de ne jamais avoir à s’inquiéter de manquer de valeurs.

MatthewAujourd'hui
la source
6

Comme d'autres personnes ont plaidé en faveur d'une clé primaire incrémentielle, je vais en créer une pour un GUID:

  • Il est garanti d'être unique
  • Vous pouvez avoir une visite de moins dans la base de données pour les données de votre application. (Pour une table de types, par exemple, vous pouvez stocker le GUID dans l’application et l’utiliser pour récupérer l’enregistrement. Si vous utilisez une identité, vous devez interroger la base de données par son nom et j’ai vu de nombreuses applications le faisant pour obtenir le PK. et plus tard interroge à nouveau pour obtenir les détails complets).
  • C'est utile pour cacher des données. www.domain.com/Article/2 Faites-moi savoir que vous n'avez que deux articles alors que www.domain.com/article/b08a91c5-67fc-449f-8a50-ffdf2403444a ne me dit rien.
  • Vous pouvez facilement fusionner des enregistrements de différentes bases de données.
  • MSFT utilise GUIDS pour l'identité.

Edit: Duplicate Point

Logique à trois valeurs
la source
5
-1. Un GUID / UUID n'est pas garanti d'être unique et n'est pas unique à 100%. Un GUID est toujours une longueur finie, vous pouvez donc risquer d'obtenir un duplicata, bien que cela soit hautement improbable. Votre remarque concernant moins de déplacements dans la base de données est également invalide. Pourquoi ne pouvez-vous pas stocker l'identifiant principal dans l'application, comme vous le pouvez avec la clé GUID?
Niklas H
2
Jeff Atwood le dit bien mieux que jamais. blog.codinghorror.com/primary-keys-ids-versus-guids
Three Value Logic
Pourquoi ne pouvez-vous pas stocker l'identifiant principal dans votre application? Parce que la base de données le crée. Si vous exécutez vos graines sur une base de données vide, vous pouvez supposer que l'ID sera 1. Que se passe-t-il si vous exécutez le même script sur une base de données contenant des données? L'ID ne sera pas 1.
Three Value Logic
Vous n'avez rien dit sur la création d'identifiants dans l'application, vous venez d'écrire "stockage". Mais s'il est nécessaire de créer l'ID en dehors de la base de données, alors oui, un GUID pourrait être la réponse.
Niklas H
2
J'ajouterais qu'ils s'échelonnent mieux. Les bases de données Big Data NoSQL telles que Cassandra ne prennent même pas en charge les clés à incrémentation automatique.
Karl Bielefeldt
2

En tant que principe de bonne conception, chaque table doit disposer d’un moyen fiable d’identifier de manière unique une ligne. Bien que ce soit le cas pour une clé primaire, cela n’exige pas toujours l’existence d’une clé primaire. L'ajout d'une clé primaire à chaque table n'est pas une mauvaise pratique car il permet l'identification unique des lignes, mais peut s'avérer inutile.

Pour maintenir des relations fiables entre les lignes de deux ou plusieurs tables, vous devez le faire via des clés étrangères, d'où le besoin de clés primaires dans au moins certaines tables. L'ajout d'une clé primaire à chaque table facilite l'extension de la conception de votre base de données lorsque vient le temps d'ajouter de nouvelles tables ou de nouvelles relations aux données existantes. La planification est toujours une bonne chose.

En règle générale (règle dure peut-être), la valeur d'une clé primaire ne doit jamais changer pendant la durée de vie de sa ligne. Il est sage de supposer que toutes les données métier d'une ligne sont sujettes à modification au cours de leur durée de vie. Par conséquent, toutes les données métier seront un mauvais candidat pour une clé primaire. C'est pourquoi quelque chose d'abstrait comme un entier auto-incrémenté est souvent une bonne idée. Cependant, les entiers auto-incrémentés ont leurs limites.

Si vos données n'auront qu'une vie dans votre base de données, les entiers auto-incrémentés conviennent. Toutefois, comme cela a été mentionné dans d’autres réponses, si vous souhaitez que vos données soient partagées, synchronisées ou aient une vie en dehors de votre base de données, les entiers auto-incrémentés génèrent de mauvaises clés primaires. Un meilleur choix sera un guid (aka uuid "universellement unique").

Zenilogix
la source
2

La question, et de nombreuses réponses, néglige le point important selon lequel toutes les clés naturelles de chaque table résident uniquement dans le schéma logique de la base de données et que toutes les clés de substitution de chaque table résident uniquement dans le schéma physique de la base de données. d'autres réponses traitent uniquement des avantages relatifs des clés de substitution GUID de nombre entier par rapport aux clés de substitution, sans expliquer les raisons pour lesquelles les clés de substitution sont correctement utilisées et quand.

BTW: Évitons d'utiliser le terme clé primaire mal défini et imprécis . Il s'agit d'un artefact de modèles de données pré-relationnels qui a d'abord été coopté (imprudemment) dans le modèle relationnel, puis coopté dans le domaine physique par divers fournisseurs de SGBDR. Son utilisation ne sert qu'à confondre la sémantique.

Notez, dans le modèle relationnel, que, pour que le schéma logique de la base de données soit de la première forme normale , chaque table doit avoir un ensemble de champs visible par l' utilisateur, appelé clé naturelle, identifiant de manière unique chaque ligne de la table. Dans la plupart des cas, une telle clé naturelle est facilement identifiée, mais il est parfois nécessaire de la construire, que ce soit comme champ de départage ou autrement. Cependant, une telle clé construite est toujours visible par l'utilisateur et réside donc toujours dans le schéma logique de la base de données.

En revanche, toute clé de substitution sur une table réside uniquement dans le schéma physique de la base de données (et doit donc toujours, pour des raisons de sécurité et pour le maintien de l'intégrité de la base de données, être totalement invisible pour les utilisateurs de la base de données). La seule raison d'introduire une clé de substitution est de résoudre les problèmes de performances liés à la maintenance physique et à l'utilisation de la base de données; qu'il s'agisse de jointures, de réplication, de sources matérielles multiples pour les données ou autre.

Puisque la performance est l’unique raison de l’introduction d’une clé de substitution, supposons que nous souhaitons qu’elle soit performante. Si le problème de performances est le problème des jointures, nous souhaitons nécessairement rendre notre clé de substitution aussi étroite que possible (sans nuire au matériel, les entiers et les octets courts sont donc généralement supprimés). Les performances de jointure reposent sur une hauteur d’index minimale; un entier de 4 octets est donc une solution naturelle. Si votre problème de performance est le taux d'insertion, un entier de 4 octets peut également constituer une solution naturelle (selon les éléments internes de votre SGBDR). Si le problème de performances d'une table est lié à la réplication ou à plusieurs sources de données plutôt qu'à une autre technologie de clé de substitution , un GUID ou une clé à deux éléments (ID hôte + entier) peut être plus approprié. Personnellement, je ne suis pas un favori des GUID, mais ils sont pratiques.

En résumé, toutes les tables n’auront pas besoin d’une clé de substitution (de tout type); ils ne doivent être utilisés que lorsque cela est jugé nécessaire pour la performance de la table considérée. Quelle que soit la technologie de substitution commune que vous préférez, réfléchissez bien aux besoins réels de la table avant de faire un choix; changer le choix de la technologie de substitution pour une table sera un travail épuisant. Documentez les indicateurs de performance clés de votre table afin que vos successeurs comprennent les choix qui ont été faits.

Cas spéciaux

  1. Si vos besoins opérationnels imposent une numérotation séquentielle des transactions à des fins d'audit (ou autres), ce champ n'est pas une clé de substitution; c'est une clé naturelle (avec des exigences supplémentaires). Dans la documentation, un entier auto-incrémenté ne génère que des clés de substitution . Trouvez donc un autre mécanisme pour le générer. De toute évidence, une sorte de moniteur sera nécessaire, et si vous effectuez le sourçage de vos transactions sur plusieurs sites, un site sera spécial en raison de son statut d' hôte désigné pour le moniteur.

  2. Si votre table ne sera jamais supérieure à une centaine de lignes, la hauteur de l'index n'est pas pertinente. chaque accès se fera par un balayage de table. Toutefois, les comparaisons de chaînes longues seront encore beaucoup plus coûteuses que la comparaison d'un entier de 4 octets et plus coûteuses que la comparaison d'un GUID.

  3. Une table de valeurs codées par un champ de code char (4) devrait être aussi performante qu'une table avec un entier de 4 octets. Bien que je n’aie aucune preuve de cela, j’utilise fréquemment cette hypothèse et je n’ai jamais eu de raison de la regretter.

Pieter Geerkens
la source
-1

Non seulement ce n'est pas une bonne pratique, mais c'est en fait décrit comme un anti-motif dans le livre SQL Antipatterns de Bill Karwin.

Toutes les tables n'ont pas besoin d'un pseudokey - une clé primaire avec une valeur arbitraire, pas quelque chose qui a une valeur sémantique pour le modèle - et il n'y a aucune raison de toujours l'appeler id.

Pedro Werneck
la source
cela ne semble rien offrir de substantiel sur les points soulevés et expliqués dans les 9 réponses précédentes
gnat
2
et pourquoi cela pourrait être important?
moucher
3
@gnat Parce que c'est un livre sur les meilleures pratiques, qui aborde directement la question. N'est-ce pas évident?
Pedro Werneck
3
pas le moindre. Recherche Google pour « réserver sql meilleures pratiques » montre sur 900K liens vers moi, pourquoi celui - ci est particulièrement digne
moucheron
1
@gn je ne vais pas discuter toute la journée. Vous n’aimez pas la réponse, c’est à cela que servent les votes négatifs.
Pedro Werneck
-2

C'est assez universel - sinon, vous devrez valider que la clé est réellement unique. Cela se ferait en regardant toutes les autres clés ... ce qui prendrait du temps. Avoir une clé incrémentielle coûte cher à mesure que votre numéro d'enregistrement approche de la valeur de dépassement de clé.

Je fais habituellement aux pointeurs des noms de champs plus évidents, comme ref_{table}des idées similaires.

S'il n'est pas nécessaire de pointer en externe sur un enregistrement, vous n'avez pas besoin d'un identifiant.

Johnny V
la source
Valeur de roulement clé?
AJJ
Un nombre entier non signé a une valeur maximale de 4294967295 avant l'ajout de 1 et le remet à 0. N'oubliez pas que si vous ajoutez un enregistrement puis le supprimez, le compteur est toujours augmenté. Assurez-vous que vous utilisez unsigned intpour le type de champ sinon la limite est la moitié de ce nombre.
Johnny V
Débordement d'entier - fr.wikipedia.org/wiki/overflow_infini
Johnny V
2
Si vous ajoutez / supprimez beaucoup de lignes, le compteur d'incrémentation automatique débordera éventuellement.
Johnny V
1
Comment les gens gèrent-ils le renversement? Que se passe-t-il s'il y a des enregistrements avec un ID faible qui ne sont jamais supprimés, mais que vous commencez vers la fin où certains ID se situent à l'extrémité supérieure de 4294967295? Peut-on effectuer une "réindexation"?
AJJ
-2

Je ne dirais pas que cela devrait toujours être fait. J'ai une table ici sans clé unique - et elle n'en a pas besoin. C'est un journal d'audit. Il n'y aura jamais de mise à jour, les requêtes renverront tous les changements apportés à ce qui est consigné mais c'est le meilleur qui puisse raisonnablement être fait. Il faut un humain pour définir un changement injustifié. (Si le code le permettait, il l'aurait refusé en premier lieu!)

Loren Pechtel
la source
-3

Un compteur d'incrémentation automatique pour une clé primaire n'est pas une bonne idée. En effet, vous devez revenir à la base de données pour rechercher la clé suivante et l'incrémenter d'une unité avant d'insérer vos données.

Cela étant dit, j'utiliserais généralement tout ce que la base de données peut fournir pour la clé primaire plutôt que de l'intégrer à l'application.

En laissant la base de données la fournir de manière native, vous pouvez garantir que la clé est unique pour ce dont elle a besoin.

Bien sûr, toutes les bases de données ne le supportent pas. Dans ce cas, j'utilise généralement une table qui stocke des compartiments de clés et utilise des plages hautes et basses gérées dans l'application. C'est la solution la plus performante que je trouve car vous obtenez une plage de 10000 nombres et vous les incrémentez automatiquement sur l'instance de l'application. Une autre instance d’application peut choisir un autre panier de nombres avec lequel travailler. Vous avez besoin d'une primitive de clé primaire suffisamment grande, telle qu'une longueur de 64 bits.

Les UUID que je n'utilise pas comme clés primaires car le coût de leur construction et de leur stockage est beaucoup plus élevé que celui de l'incrémentation d'une valeur longue par un. Les UUID traitent toujours du paradoxe de l'anniversaire en ce sens qu'un duplicata peut théoriquement survenir.

Archimedes Trajano
la source
3
N ° incrémentation automatique signifie que l'incrémentation de la clé est effectuée automatiquement par la base de données. Parfois (je vous regarde, Oracle!), Vous avez besoin d’une combinaison séquence-déclencheur, mais vous n’avez jamais besoin de rechercher la valeur précédemment insérée pour la clé, d’ajouter 1, puis de l’utiliser.
SQB
Avec certains frameworks de persistance tels que JPA, si vous souhaitez renvoyer la valeur de la clé qui a été créée à l'appelant, vous devez charger l'enregistrement afin de voir la clé.
Archimedes Trajano