Dois-je ajouter un champ d'incrémentation automatique / IDENTITÉ à une table de références croisées uniquement à des fins PK?

9

J'ajoute la table de références croisées suivante à ma base de données hébergée par SQL Server:

company_id bigint not null (FK)
org_path nvarchar (2048) not null

Le company_idchamp fait référence au idchamp d'une autre table (dans laquelle il s'agit de la clé primaire).

Étant donné qu'il peut également y avoir plusieurs enregistrements avec le même company_id, toute clé primaire devrait utiliser les deux champs. Cependant, je ne parviens pas à créer une clé à l'aide des deux champs car org_pathc'est trop long pour SQL Server.

Quant à org_path, c'est le seul tableau dans lequel il existe. Il est fort probable que les requêtes dans ce tableau demandent toutes les entrées ou toutes les org_pathentrées par company_id. Ou, pour le dire autrement, il semble douteux que ce tableau soit jamais interrogé org_path. De plus, il est peu probable qu'il org_pathsoit mis à jour, et plus probablement inséré et - probablement rarement - supprimé.

Je m'attends à ce que le nombre total de lignes soit dans les milliers bas.

En outre, la raison en nvarchar (2048)est que la valeur doit imiter cela dans une base de données tierce. Un exemple typique sera quelque chose comme

\Translation Providers\[customer name]\[order name]\

et peut contenir des diacritiques.

Donc, ma question est la suivante: serait-il plus efficace d'ajouter un idchamp d' incrémentation automatique et de l'utiliser en conjonction avec company_idcomme clé primaire, ou cela ajouterait-il une surcharge inutile - et le fait que company_idla clé primaire d'une autre table ait effet ici?

awj
la source

Réponses:

7

Pour un index cluster non unique comany_iduniquement, SQL Server ajoutera automatiquement un uniqueificateur entier de 4 octets à toutes les clés d'index cluster en double (c'est-à-dire le deuxième et les suivants pour une valeur de clé) pour le rendre unique. Cependant, cela n'est pas exposé à l'utilisateur.

L'avantage d'ajouter votre propre identifiant unique en tant que colonne de clé secondaire est que vous pouvez alors toujours rechercher par, company_idmais également rechercher des lignes individuelles plus efficacement (en utilisant company_id, identitycolplutôt company_idqu'avec un prédicat résiduel activé org_path). L'index clusterisé serait alors unique company_id, identitycol, donc aucun identificateur masqué ne serait ajouté.

De plus, si vous vous retrouvez avec des doublons pour (company_id,org_path), avoir la colonne d'identité explicite (une sorte d '"identificateur exposé") facilitera le ciblage d'un seul d'entre eux pour suppression ou mise à jour.

Martin Smith
la source
12

Une chose à considérer est qu'une clé primaire et un index cluster ne sont pas la même chose. Une clé primaire est une contrainte et traite des règles de vie des données (c'est-à-dire l'intégrité des données); cela n'a rien à voir avec l'efficacité / la performance. Une clé primaire nécessite que la ou les colonnes de clé soient uniques (en combinaison) et NON NULES (individuellement). Un PK est appliqué via un index unique, bien qu'il puisse être en cluster ou non en cluster.

Un index clusterisé est un moyen de classer physiquement (c'est-à-dire sur disque) les données de la table et traite des performances; cela n'a rien à voir avec l'intégrité des données. Un index clusterisé peutexiger que la ou les colonnes clés soient uniques (en combinaison), mais ce n'est pas nécessaire. Cependant, étant donné que l'index cluster est l'ordre physique des données, il doit identifier de manière unique chaque ligne, quoi qu'il arrive. Donc, si vous ne le définissez pas pour exiger l'unicité, il créera sa propre unicité via une colonne "uniquifier" de 4 octets cachée. Cette colonne est toujours présente dans les index cluster non uniques, mais elle ne prend pas de place lorsque les champs clés sont uniques (en combinaison). Pour voir de première main comment fonctionne cette colonne "uniquifier" (à la fois dans l'index clusterisé et l'effet sur les index non clusterisés), veuillez consulter ce script de test que j'ai publié sur PasteBin: script T-SQL pour tester la taille de l'uniquificateur .

D'où la question principale de:

serait-il plus efficace d'ajouter un idchamp d' incrémentation automatique et de l'utiliser en conjonction avec company_idcomme clé primaire, ou ajouterait-il une surcharge inutile

confond ces deux concepts, ils doivent donc être traités séparément, bien qu'il y ait certainement un certain chevauchement.

Faut- IDENTITYil ajouter une colonne ou serait-ce une surcharge inutile?

Si vous ajoutez une INT IDENTITYcolonne et l'utilisez pour créer un PK, en supposant qu'il s'agisse d'un PK en cluster, cela ajoute 4 octets à chaque ligne. Cette colonne est visible et utilisable dans les requêtes. Il pourrait être ajouté à d'autres tables en tant que clé étrangère, mais dans ce cas particulier, cela ne se produira pas.

Si vous n'ajoutez pas la INT IDENTITYcolonne, vous ne pouvez pas créer de PK sur cette table. Cependant, vous pouvez toujours créer un index cluster sur la table tant que vous n'utilisez pas l' UNIQUEoption. Dans ce cas, SQL Server ajoutera une colonne masquée appelée "uniquifier" qui se comporte comme décrit ci-dessus. Étant donné que la colonne est masquée, elle ne peut pas être utilisée dans les requêtes ou comme référence pour les clés étrangères.

En ce qui concerne l'efficacité, ces options sont à peu près les mêmes. Oui, il y aura un peu moins d'espace occupé en ayant l'index cluster non unique en raison du fait que certaines lignes (celles avec les valeurs de clé uniques initiales) IDENTITYoccupent 0 octet tandis que toutes les lignes dans / PK prendront les 4 octets. Mais il n'y aura pas assez de lignes de 0 octet (en particulier avec la petite quantité de lignes attendue) pour remarquer une différence, sans parler de la commodité de pouvoir utiliser la IDcolonne dans les requêtes.

INT IDENTITY Column ou Hash of org_pathPersisted Computed Column?

Étant donné que vous ne rechercherez pas de lignes en fonction de org_pathvaleurs, il n'est pas logique d'ajouter la surcharge de la colonne calculée persistante et de devoir calculer ce hachage dans les requêtes afin de les comparer à la colonne calculée (c'était ma suggestion originale, disponible dans l'historique des révisions ici , qui était basée sur le libellé initial / les détails de la question). Dans ce cas particulier, la INT IDENTITYcolonne "ID" est probablement la meilleure.

Ordre des colonnes clés

Étant donné que la IDcolonne sera rarement, voire jamais, utilisée dans les requêtes, et étant donné que les deux principaux cas d'utilisation sont d'obtenir "toutes les lignes" ou "toutes les lignes pour une donnée company_id", je créerais le PK company_id, id. Et parce que cela signifie que les lignes ne sont pas insérées séquentiellement, je spécifierais un FILLFACTORde 90. Vous devrez également vous assurer de faire une maintenance d'index régulière pour réduire la fragmentation.

Deuxième question

le fait que company_id est la clé primaire dans une autre table a-t-il un effet ici

Non.

Déclencheur

Étant donné que les org_pathvaleurs dans un company_idsont uniques, vous devez toujours créer un déclencheur INSERT, UPDATEpour appliquer ce paramètre. Dans le déclencheur, faites un IF EXISTSavec une requête qui fait probablement un COUNT(*)et GROUP BY company_id, org_path. Si quelque chose est trouvé, émettez un ROLLBACKpour annuler l'opération DML, puis RAISERRORdites qu'il y a des doublons.

Collation

Dans ma réponse initiale (basée sur le libellé original / des détails clairsemés de la question, et disponibles dans l'historique des révisions ici ), j'avais suggéré d'utiliser éventuellement un classement binaire (c'est-à-dire _BIN2). Maintenant que nous avons une idée de ce qu'est exactement org_path, je ne recommanderais pas d' utiliser un classement binaire. Comme il y aura des signes diacritiques, vous ne voulez utiliser des équivalences linguistiques.

Solomon Rutzky
la source
Continuons cette discussion dans le chat .
Solomon Rutzky
0

Pourquoi avez-vous besoin d'un PK?

Pourquoi ne pas simplement utiliser company_id comme index non clusterisé?

Vous avez dit que la plupart des recherches se faisaient sur toutes les entrées ou par company_id Mettre à
jour
rarement Rarement supprimer
org_path, c'est le seul tableau dans lequel il existe

La réponse de Martin Smith peut vous fournir ce dont vous avez besoin
Je ne suis pas familier avec l'ajout automatique d'un uniqueificateur d'entier de 4 octets
Peut-être qu'il me manque quelque chose mais si vous n'avez pas d'autres colonnes indexées, je ne vois aucun intérêt à cela dans ce cas d'utilisation

Si vous êtes préoccupé par DRI, les tables doivent utiliser la table Company comme FK pour company_id

paparazzo
la source
Hey. Concernant " Pourquoi ne pas simplement utiliser company_id comme un index non clusterisé? ": Parce que cela aurait 2 inconvénients: 1) ce serait 1 chose de plus qui prend de la place alors qu'un index clusterisé est la table, donc pas d'élément supplémentaire, et 2) il faudrait toujours une recherche RID pour obtenir le champ NVARCHAR, à moins qu'il ne s'agisse d'une INCLUDEcolonne, mais c'est encore pire car il s'agit simplement de dupliquer la table. Certes, le PK n'est pas nécessaire; la partie importante est l'indice clusterisé. Mais une fois que vous avez l'IDENTITE, autant aller avec PK. Et s'il vous plaît voir le nouveau lien dans ma réponse pour une visite guidée sur Uniquifier 😃
Solomon Rutzky
@srutzky Mais cela évite un uniqueificateur entier de 4 octets donc je vois ça comme un lavage
paparazzo
Avec moins de 10 000 lignes, cela n'aura pas d'importance; vous devez probablement être dans les millions de lignes avant de remarquer l'effet de seulement 4 octets. Ainsi, pour la requête "obtenir toutes les lignes", il n'y a pas vraiment de différence dans l'une de ces options. Mais pour la requête "get for company_id = @param", le fait d'avoir physiquement ordonné les données par company_id sera utile, surtout quand il n'est pas nécessaire d'effectuer une recherche RID pour chaque ligne.
Solomon Rutzky
@srutzky Wash est un lavage - 10K ou 1G. C'est juste quelque chose que le PO doit considérer.
paparazzo