Colonne NVARCHAR en tant que PRIMARY KEY ou en tant que colonne UNIQUE

11

Je développe une base de données SQL Server 2012 et j'ai un doute sur les colonnes nvarchar comme clés primaires.

J'ai ce tableau:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Mais maintenant, je veux utiliser la [CODE]colonne comme clé primaire et supprimer la [ID_CODE]colonne.

Y a-t-il un problème ou une pénalité si j'ai une NVARCHARcolonne en tant que PRIMARY KEY?

[CODE]la valeur de la colonne doit être unique, j'ai donc pensé pouvoir définir une UNIQUEcontrainte sur cette colonne.

Dois-je utiliser [CODE]comme clé primaire ou il est préférable de définir une UNIQUEcontrainte sur la [CODE]colonne?

VansFannel
la source
1
Une chose assez importante à considérer est le nombre de lignes qu'il y aura dans votre table?
James Z
Ce n'est pas une réponse en soi , mais j'ai tendance à penser que votre CODEcolonne doit être unique, mais pas une clé primaire. Je soupçonne qu'il contient des informations. Si ces informations CODEpeuvent être modifiées de quelque manière que ce soit, alors votre information devrait changer ou être périmée. Cela rendrait votre clé primaire volatile, et je ne vois pas bien cela se terminer. Il vaut mieux laisser votre PK être une clé et votre CODE peut faire ce qu'il veut. Juste une opinion.
Manngo
@Manngo, merci pour votre commentaire. Oui, je l'ai fait de cette façon: ID_CODE est la clé primaire et CODE est UNIQUE.
VansFannel

Réponses:

13

Oui, il y a absolument des conséquences négatives à utiliser une chaîne au lieu d'un type numérique pour une clé primaire, et plus encore si ce PK est en cluster (ce qui est effectivement le cas dans votre cas). Cependant, la mesure dans laquelle vous voyez les effets de l'utilisation d'un champ de chaîne est fonction de a) du nombre de lignes de cette table et b) du nombre de lignes des autres tables qui sont à clé étrangère pour ce PK. Si vous n'avez que 10 000 lignes dans cette table et 100 000 lignes dans quelques autres tables qui FK vers cette table via ce champ, alors ce ne sera peut-être pas si visible. Mais ces effets deviennent certainement plus visibles à mesure que le nombre de lignes augmente.

Vous devez tenir compte du fait que les champs d'un index cluster sont reportés sur les index non cluster. Donc, vous ne regardez pas seulement jusqu'à 40 octets par ligne, mais (40 * un_nombre) octets. Et dans toutes les tables FK, vous avez ces mêmes 40 octets dans la ligne, plus souvent qu'autrement, il y aura un index non clusterisé sur ce champ car il est utilisé dans JOINs, donc maintenant il est vraiment doublé dans toutes les tables que FK à celui-là. Si l'on est enclin à penser que 40 octets * 1 million de lignes * 10 copies ne sont rien à craindre, veuillez consulter mon article Disk Is Cheap! ORLY? qui détaille tous (ou au moins la plupart) des domaines touchés par cette décision.

L'autre chose à considérer est que le filtrage et le tri sur les chaînes, en particulier lorsque vous n'utilisez pas un classement binaire (je suppose que vous utilisez la base de données par défaut qui est généralement insensible à la casse) est beaucoup moins efficace (c'est-à-dire prend plus de temps) que lorsque vous utilisez INT/ BIGINT. Cela a un impact sur toutes les requêtes qui filtrent / joignent / trient sur ce champ.

Par conséquent, utiliser quelque chose comme CHAR(5)serait probablement OK pour un PK en cluster, mais surtout s'il était également défini avec COLLATE Latin1_General_100_BIN2(ou quelque chose comme ça).

Et la valeur de [CODE]jamais peut-elle changer? Si oui, c'est encore plus de raison de ne pas l'utiliser comme PK (même si vous définissez les FK sur ON UPDATE CASCADE). Si cela ne peut pas ou ne changera jamais, c'est bien, mais il y a encore plus de raisons de ne pas l'utiliser comme PK en cluster.

Bien sûr, la question peut être mal formulée car il semble que vous ayez déjà ce champ dans votre PK.

Quoi qu'il en soit, votre meilleure option, de loin, est d'utiliser [ID_CODE]comme Clustered PK, d'utiliser ce champ dans les tables connexes comme FK et de le conserver [CODE]comme UNIQUE INDEX(ce qui signifie qu'il s'agit d'une "clé alternative").


Mise à jour
Un peu plus d'informations basées sur cette question dans un commentaire sur cette réponse:

[ID_CODE], en tant que CLÉ PRIMAIRE, est-il la meilleure option si j'utilise la colonne [CODE] pour rechercher la table?

Tout cela dépend d'un grand nombre de facteurs, dont certains que j'ai déjà mentionnés mais qui seront reformulés:

Une clé primaire est la façon dont la ligne individuelle est identifiée, qu'elle soit référencée ou non par des clés étrangères. La façon dont votre système identifie la ligne en interne est liée, mais pas nécessairement la même que, à la façon dont vos utilisateurs s'identifient / cette ligne. Toute colonne NOT NULL avec des données uniques pourrait fonctionner, mais il y a des problèmes pratiques à prendre en compte, surtout si le PK est, en fait, référencé par des FK. Par exemple, les GUID sont uniques et certaines personnes aiment vraiment les utiliser pour diverses raisons, mais elles sont assez mauvaises pour les index clusterisés ( NEWSEQUENTIALIDc'est mieux, mais pas parfait). D'un autre côté, les GUID sont très bien comme clés alternatives et utilisés par l'application pour rechercher la ligne, mais les JOIN se font toujours à l'aide d'un PK INT (ou similaire).

Jusqu'à présent, vous ne nous avez pas dit comment le [CODE]champ s'intègre dans le système sous tous les angles, en dehors de mentionner maintenant que c'est ainsi que vous recherchez les lignes, mais est-ce pour toutes les requêtes ou juste pour certaines? Par conséquent:

  • Concernant la [CODE]valeur:

    • Comment est-il généré?
    • Est-ce incrémental ou pseudo-aléatoire?
    • Est-ce une longueur uniforme ou une longueur variable?
    • Quels caractères sont utilisés?
    • Si vous utilisez des caractères alphabétiques: est-il sensible à la casse ou insensible?
    • Peut-il jamais changer après avoir été inséré?
  • Concernant ce tableau:

    • Y a-t-il d'autres tables FK à cette table? Ou ces champs ( [CODE]ou [ID_CODE]) sont-ils utilisés dans d'autres tables, même s'ils ne sont pas explicitement à clé étrangère?
    • Si [CODE] le seul champ est utilisé pour obtenir des lignes individuelles, à quoi sert le [ID_CODE]champ? S'il n'est pas utilisé, pourquoi l'avoir en premier lieu (ce qui pourrait dépendre de la réponse "Le [CODE]champ peut-il jamais changer?")?
    • Combien de lignes dans ce tableau?
    • Si d'autres tableaux font référence à ce tableau, combien et combien de lignes dans chacun d'eux?
    • Quels sont les index de cette table?

Cette décision ne peut pas être prise uniquement sur la question "NVARCHAR oui ou non?". Je dirai encore que, d'une manière générale, je ne trouve pas que ce soit une bonne idée, mais il y a certainement des moments où c'est bien. Étant donné le peu de champs dans ce tableau, il est peu probable qu'il y ait plus, ou du moins pas beaucoup, d'index. Donc, vous pourriez être d'accord dans les deux cas [CODE]comme index clusterisé. Et si aucune autre table ne fait référence à cette table, vous pouvez également en faire le PK. Mais, si d'autres tables font référence à cette table, j'opterais pour le [ID_CODE]champ comme PK, même s'il n'est pas en cluster.

Solomon Rutzky
la source
Est-ce que l'électeur anonyme (qui semble également avoir voté contre la réponse de @noIDonthissystem) voudrait proposer une critique constructive ou signaler une logique erronée?
Solomon Rutzky
Merci pour votre réponse. Est-ce [ID_CODE], as PRIMARY KEY, la meilleure option si j'utilise la [CODE]colonne pour rechercher la table?
VansFannel
@VansFannel s'il vous plaît voir ma mise à jour. Merci.
Solomon Rutzky
J'ai rejoint cette communauté dba pour simplement voter pour cette réponse.
Ahmet Arslan
6

Vous devez séparer les concepts:

  • La clé primaire est un concept de conception , une propriété logique des entrées du tableau. Elle doit être immuable pendant la durée de vie de l'entrée de table et doit être la clé utilisée dans l'application pour référencer l'entrée.

  • L'index cluster est un concept de stockage , une propriété physique. Il doit être le chemin d'accès le plus courant pour les requêtes, il doit servir à satisfaire comme index de couverture pour la plupart des cas et à satisfaire autant de requêtes de plage que possible.

N'est pas requis pour que la clé primaire soit l'index cluster. Vous pouvez avoir ID_CODEcomme (CODE_LEVEL, CODE)clé PK et comme clé en cluster. Ou l'inverse.

Une clé en cluster plus grande a des répercussions négatives, car la clé plus large signifie une densité plus faible sur les pages d'index et une plus grande taille consommée sur tous les index non cluster. il y a déjà eu des tonnes d'encre renversées sur ce sujet, par exemple. Partir de Plus de considérations sur la clé de clustering - le débat sur l'index clusterisé continue! .

Mais l'essentiel est que le choix de la clé d'index cluster est principalement un compromis. D'une part, vous avez des exigences de taille de stockage, avec des répercussions générales sur les performances (clé plus grande -> taille plus grande -> plus d'E / S et la bande passante d'E / S est probablement la ressource la plus rare dont vous disposez). D'un autre côté, le choix de la mauvaise clé en cluster au nom des économies d'espace peut avoir des conséquences sur les performances des requêtes, souvent pires que les problèmes résultant d'une clé large.

Quant au choix de la clé primaire, il ne devrait même pas être un problème: votre modèle de données, la logique de votre application, doivent dicter ce qu'est la clé primaire.

Cela étant dit, mon 2c: NVARCHAR(20)n'est pas large. Est une taille de clé en cluster parfaitement acceptable, même pour une grande table.

Remus Rusanu
la source
Merci pour votre réponse. Est-ce [ID_CODE], as PRIMARY KEY, la meilleure option si j'utilise la [CODE]colonne (et peut-être [CODE_LEVEL]) pour rechercher la table?
VansFannel
@VansFannel vous seul pouvez y répondre.
Remus Rusanu
Mais à votre avis ...
VansFannel
2
Mon avis devrait tenir compte de la DDL exacte de la table entière et de tous les index, des clés étrangères qui y font référence, du nombre estimé de lignes, de la charge de travail attendue pour les requêtes, de l'application des SLA attendus et non des moindres disponibles pour le matériel et les licences.
Remus Rusanu
Merci. J'utiliserai la [CODE]colonne comme CLÉ PRIMAIRE.
VansFannel
4

Je n'autoriserais jamais personne à créer nvarchar(20)un PK dans ma base de données. Vous gaspillez de l'espace disque et de la mémoire cache. Chaque index de cette table et tous les FK qui y sont répliquent cette valeur large. Peut-être un char (20) s'ils peuvent le justifier. Dans quel type de données essayez-vous de stocker CODE? Avez-vous vraiment besoin de stocker des caractères nvarchar? J'ai tendance à rendre les valeurs «internes» des PK non vues par les utilisateurs, et j'essaie de garder les valeurs affichées séparément. Les valeurs affichées doivent parfois être modifiées, ce qui devient très problématique avec les PK + FK.

Vous rendez-vous également compte qu'une «identité bigint (1,1)» peut augmenter jusqu'à 9 223 372 036 854 775 807?

[ID_CODE] [bigint] IDENTITY(1,1)

À moins que vous ne construisiez cette base de données pour Google, une normale int identity (1,1)avec sa limite de plus de 2 milliards ne suffira-t-elle pas?

aucun ID sur ce système
la source
int est de 4 octets en SQL, ce qui vous donne -2,1 milliards à + 2,1 milliards.
datagod
@datagod, ha merci, tant de chiffres que j'ai mal comptés!
aucun ID sur ce système
Merci pour votre réponse. Est-ce [ID_CODE], as PRIMARY KEY, la meilleure option si j'utilise la [CODE]colonne pour rechercher la table? Merci.
VansFannel
J'étais dans ce bateau jusqu'à ce que quelqu'un utilise la nature séquentielle de "int" pour prédire les données / utilisateurs dans ma base de données et récolter presque tout ce que j'avais. Plus jamais. Le public confronté au besoin de DB doit être un peu plus difficile à obtenir des informations.
DaBlue
3

Il ne devrait y avoir aucune pénalité inhérente / perceptible autre que vous risquez d'utiliser des touches larges lorsque vous utilisez nvarchar / varchar si vous ne le savez pas. Surtout si vous commencez à les combiner dans des clés composites.

Mais dans votre exemple d'une longueur (20), vous devriez être bien et je ne m'inquiéterais pas beaucoup à ce sujet. Parce que si CODE est la façon dont vous interrogez principalement vos données - un index clusterisé qui semble très sensé.

Cependant, vous devez déterminer si vous le souhaitez réellement comme clé primaire ou simplement comme index unique (en cluster). Il y a une (petite) différence entre l'index clusterisé et la clé primaire (fondamentalement - la clé primaire identifie vos données, mais l'index est la façon dont vous interrogez les données), donc si vous le souhaitez, vous pouvez tout aussi facilement créer votre ID_Code comme clé primaire et créer un index cluster unique sur CODE. (remarque: SQL Server transformera automatiquement votre clé primaire en index cluster, sauf si vous avez créé manuellement l'index cluster vous-même)

Vérifiez également si vous avez réellement besoin d'ID_Code, vous disposez maintenant d'un CODE unique.

Allan S. Hansen
la source
2
En fait, sa taille NVARCHAR(20)est de 40 octets (max), et comme il s'agit d' une colonne de longueur variable , ce n'est pas vraiment le meilleur choix pour un index clusterisé. ID_CODEêtre un BIGINT IDENTITYserait le bien meilleur choix ici!
marc_s
Je sais que c'est 40 octets, mais il n'y avait pas beaucoup de raisons de le spécifier, car il est loin des 900 octets. Et si vous interrogez principalement les données de CODE, ce serait un meilleur choix pour éviter d'avoir des index redondants à maintenir, car vous auriez toujours besoin d'un index dessus, puis vous devriez rechercher dans le cluster vers l'arrière
Allan S. Hansen
Il convient de mentionner - que j'ai oublié de mentionner et que je soupçonne que @marc_s s'adresse est qu'un index de ce type peut conduire à une fragmentation d'index plus grande qu'une identité séquentielle, mais je le vois toujours comme un index sensible dans cette situation spécifique basée sur le facteur d'interrogation.
Allan S. Hansen