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 colonne [CODE]
Comme clé primaire et supprimer la colonne [ID_CODE]
.
Y a-t-il un problème ou une pénalité si j'ai une colonne NVARCHAR
en tant que PRIMARY KEY
?
La valeur de la colonne [CODE]
Doit être unique, j'ai donc pensé pouvoir définir une contrainte UNIQUE
sur cette colonne.
Dois-je utiliser [CODE]
Comme clé primaire ou est-il préférable de définir une contrainte UNIQUE
sur la colonne [CODE]
?
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 10k lignes dans ce tableau et 100k lignes dans quelques autres tables qui FK à 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 * some_number) octets. Et dans toutes les tables FK, vous avez ces mêmes 40 octets dans la ligne et plus souvent qu'autrement, il y aura un index non cluster 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 pas préoccupantes, veuillez consulter mon article Disk Is Cheap! ORLY? qui détaille tout (ou du moins la plupart ) des zones touchées 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 affecte 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 changé? Si oui, c'est une raison de plus pour 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 PK en cluster, utilisez ce champ dans les tables associées comme FK et conservez [CODE]
comme un 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 en interne la ligne 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 fonctionne, 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 ils sont assez mauvais pour les index clusterisés (NEWSEQUENTIALID
est meilleur, 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:
En ce qui concerne la [CODE]
valeur:
Concernant ce tableau:
[CODE]
ou [ID_CODE]
) utilisé dans d'autres tables, même s'il n'est pas explicitement à clé étrangère?[CODE]
est le seul champ utilisé pour obtenir des lignes individuelles, alors à quoi sert le [ID_CODE]
service sur le terrain? S'il n'est pas utilisé, pourquoi l'avoir en premier lieu (qui pourrait dépendre de la réponse à "Est-ce que le [CODE]
le champ a-t-il changé? ")?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 peu, d'index. Donc, vous pourriez être bien de toute façon d'avoir [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 tableaux font référence à ce tableau, j'opterais pour le [ID_CODE]
champ comme PK, même s'il n'est pas en cluster.
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_CODE
Comme PK et (CODE_LEVEL, CODE)
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. commencer à partir de Plus de considérations pour 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 IO la bande passante est probablement la la ressource la plus rare que vous ayez. D'autre part, 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)
est pas large. Est une taille de clé en cluster parfaitement acceptable, même pour une grande table.
Je n'autoriserais jamais personne à faire d'une 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. Quel type de données essayez-vous de stocker dans CODE
? Avez-vous vraiment besoin de stocker des caractères nvarchar? J'ai tendance à rendre les valeurs PK "internes" 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 int identity (1,1)
normale avec sa limite supérieure à 2 milliards ne suffira-t-elle pas?
Il ne devrait pas y avoir de 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 un index cluster, sauf 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.