web-dev-qa-db-fra.com

Pourquoi une valeur de clé primaire changerait-elle?

J'ai fait des recherches sur le concept de ROWGUID récemment et suis tombé sur la question this . This réponse a donné un aperçu, mais m'a conduit dans un trou de lapin différent avec la mention de changer la valeur de la clé primaire.

Ma compréhension a toujours été qu'une clé primaire devrait être immuable, et ma recherche depuis la lecture de cette réponse n'a fourni que des réponses qui reflètent la même chose qu'une meilleure pratique.

Dans quelles circonstances une valeur de clé primaire devrait-elle être modifiée après la création de l'enregistrement?

19
5crammed

Si vous utilisez le nom d'une personne comme clé primaire et que son nom change, vous devez modifier la clé primaire. C'est à cela que ON UPDATE CASCADE Est utilisé car il s'agit essentiellement des cascades de la modification de toutes les tables liées qui ont des relations de clé étrangère avec la clé primaire.

Par exemple:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonKey)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonKey, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonAKAKey, PersonKey)
VALUES ('Death', 'Joe Black');

Un SELECT contre les deux tables:

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

Retour:

enter image description here

Si nous mettons à jour la colonne PersonKey et réexécutons la SELECT:

UPDATE dbo.People
SET PersonKey = 'Mr Joe Black'
WHERE PersonKey = 'Joe Black';

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

nous voyons:

enter image description here

En regardant le plan de l'instruction UPDATE ci-dessus, nous voyons clairement que les deux tables sont mises à jour par une seule instruction de mise à jour en vertu de la clé étrangère définie comme ON UPDATE CASCADE:

enter image description here cliquez sur l'image ci-dessus pour la voir plus clairement

Enfin, nous allons nettoyer nos tables temporaires:

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

Le préféré1 moyen de le faire en utilisant des clés de substitution serait:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , PersonName VARCHAR(200) NOT NULL
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonAKAName VARCHAR(200) NOT NULL
    , PersonID INT NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonID)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonName, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonID, PersonAKAName)
VALUES (1, 'Death');

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

UPDATE dbo.People
SET PersonName = 'Mr Joe Black'
WHERE PersonID = 1;

Pour être complet, le plan de l'instruction de mise à jour est très simple et montre un avantage à remplacer les clés, à savoir qu'une seule ligne doit être mise à jour par opposition à chaque ligne contenant la clé dans un naturel scénario clé:

enter image description here

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

Les résultats des deux instructions SELECT ci-dessus sont:

enter image description here

Le résultat est essentiellement le même. Une différence majeure est que la clé naturelle large n'est pas répétée dans chaque table où se trouve la clé étrangère. Dans mon exemple, j'utilise une colonne VARCHAR(200) pour contenir le nom de la personne, ce qui nécessite l'utilisation d'une VARCHAR(200) partout. S'il y a beaucoup de lignes et beaucoup de tables contenant la clé étrangère, cela ajoutera beaucoup de mémoire gaspillée. Remarque, je ne parle pas de gaspillage d'espace disque car la plupart des gens disent que l'espace disque est si bon marché qu'il est essentiellement gratuit. Cependant, la mémoire coûte cher et mérite d'être chérie. L'utilisation d'un entier de 4 octets pour la clé permettra d'économiser une grande quantité de mémoire si l'on considère la longueur moyenne du nom d'environ 15 caractères.

Tangentiel à la question sur comment et pourquoi les clés peuvent changer est la question de savoir pourquoi choisir les clés naturelles plutôt que les clés de substitution, qui est une question intéressante et peut-être plus importante, en particulier lorsque la performance est un objectif de conception. Voir ma question ici à ce sujet.


1 - http://weblogs.sqlteam.com/mladenp/archive/2009/10/06/Why-I-prefer-surrogate-keys-instead-of-natural-keys-in.aspx

25
Max Vernon

Bien que vous puissiez utiliser une clé naturelle et/ou modifiable comme votre PK, selon mon expérience, cela entraîne des problèmes, qui peuvent souvent être évités par l'utilisation d'un PK qui remplit ces conditions:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

Par exemple, de nombreuses entreprises aux États-Unis essaient d'utiliser les numéros de sécurité sociale comme numéros d'identification personnels (et PK) dans leurs systèmes. Ensuite, ils rencontrent les problèmes suivants - des erreurs de saisie de données conduisant à plusieurs enregistrements qui doivent être réparés, des personnes qui n'ont pas de SSN, des personnes dont le SSN est modifié par le gouvernement, des personnes qui ont des SSN en double.

J'ai vu chacun de ces scénarios. J'ai également vu des entreprises qui ne voulaient pas que leurs clients soient "juste un numéro", ce qui signifiait que leur PK était finalement "premier + moyen + dernier + DOB + Zip" ou une autre absurdité similaire. Bien qu'ils aient ajouté suffisamment de champs pour garantir presque l'unicité, leurs requêtes étaient horribles et la mise à jour de l'un de ces champs signifiait pourchasser les problèmes de cohérence des données.

D'après mon expérience, un PK généré par la base de données elle-même est presque toujours une meilleure solution.

Je recommande cet article pour des pointeurs supplémentaires: http://www.agiledata.org/essays/keys.html

9
Byron Jones

La clé primaire peut être modifiée lors de la synchronisation. Cela peut être le cas lorsque vous avez un client déconnecté et qu'il synchronise les données avec le serveur à certains intervalles.

Il y a quelques années, j'ai travaillé sur un système où toutes les données d'événement sur la machine locale avaient des ID de ligne négatifs, comme -1, -2, etc. Lorsque les données étaient synchronisées avec le serveur, l'ID de ligne sur le serveur était appliqué au client. Supposons que l'ID de ligne suivant sur le serveur soit 58. Ensuite, -1 deviendrait 58, -2 59 et ainsi de suite. Cette modification d'ID de ligne serait répercutée en cascade sur tous les enregistrements FK enfants sur la machine locale. Le mécanisme a également été utilisé pour déterminer les enregistrements précédemment synchronisés.

Je ne dis pas que c'était une bonne conception, mais c'est un exemple de changement de clé primaire au fil du temps.

7
Jon Raynor

Toute conception impliquant la modification du PRIMARY KEY sur une base régulière est une recette pour un désastre. La seule bonne raison de le modifier serait la fusion de deux bases de données auparavant distinctes.

Comme indiqué par @MaxVernon, des changements occasionnels peuvent se produire - utilisez alors ON UPDATE CASCADE, bien que la majorité des systèmes utilisent de nos jours un ID comme substitut PRIMARY KEY.

Des puristes tels que Joe Celko et Fabian Pascal (un site à suivre) ne sont pas d'accord avec l'utilisation des clés de substitution, mais je pense qu'ils ont perdu cette bataille particulière.

5
Vérace

Chose intéressante, la question liée sur le type de ROWGUID fournit son propre cas d'utilisation: lorsque vous avez des clés primaires en conflit dans des bases de données qui doivent être synchronisées. Si vous avez deux bases de données à réconcilier et qu'elles utilisent des séquences pour les clés primaires, vous voudrez que l'une des clés change pour qu'elle reste unique.

Dans un monde idéal, cela n'arriverait jamais. Vous utiliseriez des GUID pour les clés primaires pour commencer. De façon réaliste, cependant, vous pourriez même ne pas avoir de base de données distribuée lorsque vous commencez à concevoir, et la convertir en GUID a peut-être été un effort qui a été priorisé ci-dessous pour la faire distribuer, car elle a été considérée comme ayant un impact plus élevé que la mise en œuvre de la mise à jour clé. Cela pourrait se produire si vous avez une grande base de code qui dépend de clés entières et nécessiterait une révision majeure pour convertir en GUID. Il y a aussi le fait que les GUID clairsemés (GUID qui ne sont pas très proches les uns des autres, ce qui se produit si vous les générez de manière aléatoire comme vous le devriez) peuvent également causer des problèmes pour certains types d'index, ce qui signifie que vous voulez éviter d'utiliser les en tant que clés primaires (mentionnées par Byron Jones ).

3
jpmc26

La stabilité est une propriété souhaitable pour une clé mais c'est une chose relative et non une règle absolue. En pratique, il est souvent utile de modifier les valeurs des clés. En termes relationnels, les données ne sont identifiables que par ses (super) clés. Il s'ensuit que s'il n'y a qu'une seule clé dans une table donnée, la distinction entre A) changer une valeur de clé, ou B) remplacer l'ensemble de lignes dans une table par un ensemble de lignes similaires ou différentes contenant d'autres valeurs de clé, est essentiellement une question de sémantique plutôt que de logique.

Un exemple plus intéressant est le cas d'une table ayant plusieurs clés où les valeurs d'une ou plusieurs de ces clés peuvent devoir changer par rapport à d'autres valeurs de clé. Prenons l'exemple d'une table Employé avec deux clés: LoginName et Badge Number. Voici un exemple de ligne de ce tableau:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |47832   |
+---------+--------+

Si ZoeS perd son badge, peut-être lui en attribue-t-on un nouveau et obtient-il un nouveau numéro de badge:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |50282   |
+---------+--------+

Plus tard, elle pourrait décider de changer son nom de connexion:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZSmith   |50282   |
+---------+--------+

Les deux valeurs clés ont changé - l'une par rapport à l'autre. Notez que cela ne fait pas nécessairement de différence lequel est considéré comme "primaire".

En pratique, l '"immuabilité", c'est-à-dire qu'elle ne change absolument jamais de valeur, est irréalisable ou du moins impossible à vérifier. Dans la mesure où le changement fait une différence, le cours le plus sûr est probablement de supposer que n'importe quelle clé (ou n'importe quel attribut) devrait changer.

3
nvogel

Un scénario possible est de supposer que vous avez des affiliés qui ont un ID unique et que vous savez qu'ils ne se dupliqueront pas entre les affiliés car ils ont un caractère de départ unique. Les affiliés chargent des données dans une table principale. Ces enregistrements sont traités puis attribués à un ID maître. Les utilisateurs doivent avoir accès aux enregistrements dès qu'ils sont chargés, même s'ils ne sont pas encore traités. Vous souhaitez que l'ID maître soit basé sur la commande traitée et vous ne traiterez pas toujours dans l'ordre de chargement des enregistrements. Je sais un peu fabriqué.

0
paparazzo