clé de substitution vs clé naturelle: chiffres concrets sur les différences de performances?
-
22-07-2019 - |
Question
Il existe un débat sain entre les clés de substitution et les clés naturelles:
Mon avis, qui semble correspondre à la majorité (c’est une faible majorité), est que vous devez utiliser des clés de substitution, à moins qu’une clé naturelle ne soit tout à fait évidente et qu’elle ne garantisse pas de modification. Ensuite, vous devez imposer l'unicité de la clé naturelle. Ce qui signifie des clés de substitution presque tout le temps.
Exemple des deux approches, en commençant par une table d'entreprise:
1: Clé de substitution: la table a un champ ID qui est la PK (et une identité). Les noms de société doivent être uniques par État, il y a donc une contrainte unique.
2: Clé naturelle: la table utilise CompanyName et State comme PK - satisfait à la fois la PK et son caractère unique.
Supposons que la société PK est utilisée dans 10 autres tables. Mon hypothèse, sans chiffres à l'appui, est que l'approche par clé de substitution serait beaucoup plus rapide ici.
Le seul argument convaincant que j'ai vu pour la clé naturelle concerne une table plusieurs-à-plusieurs qui utilise les deux clés étrangères comme clé naturelle. Je pense que dans ce cas, cela a du sens. Mais vous pouvez avoir des problèmes si vous avez besoin de refactoriser; c'est hors de propos de ce post je pense.
Quelqu'un a-t-il déjà vu un article comparant les différences de performances sur un ensemble de tableaux utilisant des clés de substitution contre , le même ensemble de tableaux utilisant touches naturelles ? En regardant autour de nous et Google n'a rien rapporté qui vaille la peine, juste beaucoup de théorie.
Mise à jour importante : j'ai déjà créé un ensemble de tables de test répondant à cette question. Cela ressemble à ceci:
- PartNatural - Table des pièces utilisant le numéro de pièce unique en tant que PK
- PartSurrogate - table des pièces qui utilise un identifiant (int, identité) en tant que PK et a un index unique sur le PartNumber
- Plant - ID (int, identité) en tant que PK
- Ingénieur - ID (int, identité) en tant que PK
Chaque pièce est jointe à une usine et chaque cas d’une pièce d’une usine est associé à un ingénieur. Si quelqu'un a un problème avec ce banc d'essai, c'est le moment.
La solution
Utilisez les deux! Les clés naturelles empêchent la corruption de la base de données (incohérence pourrait être un meilleur mot). Lorsque le " right " la clé naturelle (pour éliminer les lignes en double) fonctionnerait mal en raison de la longueur ou du nombre de colonnes impliquées, une clé de substitution peut également être ajoutée pour être utilisée comme clé étrangère dans d'autres tables au lieu de la clé naturelle. Mais la clé naturelle doit rester une clé alternative ou un index unique pour éviter la corruption des données et améliorer la cohérence de la base de données ...
Une grande partie de la hoohah (dans le "débat" sur cette question) peut être due à une fausse hypothèse: vous devez utiliser la clé primaire pour les jointures et les clés étrangères dans les autres tables. C'EST FAUX. Vous pouvez utiliser N'IMPORTE QUELLE clé comme cible pour les clés étrangères dans d'autres tables. Il peut s'agir de la clé primaire, d'une clé alternative ou de tout index unique ou contrainte unique. Et en ce qui concerne les jointures, vous pouvez utiliser n'importe quoi pour une condition de jointure, il n'est même pas nécessaire que ce soit une clé, un idex ou même un élément unique! (bien que s'il n'est pas unique, vous obtiendrez plusieurs lignes dans le produit cartésien qu'il crée).
Autres conseils
La valeur des clés naturelles est différente de celle des clés de substitution, pas du type.
Tout type peut être utilisé pour une clé de substitution, comme un VARCHAR
pour le slug
généré par le système ou autre chose.
Cependant, les types les plus utilisés pour les clés de substitution sont INTEGER
et RAW (16)
(ou quel que soit le type utilisé par votre SGBDR
pour GUID
's),
La comparaison d'entiers de substitution et d'entiers naturels (comme SSN
) prend exactement le même temps.
En comparant les VARCHAR
, les collations sont prises en compte et elles sont généralement plus longues que les entiers, ce qui les rend moins efficaces.
La comparaison d'un ensemble de deux INTEGER
est probablement également moins efficace que la comparaison d'un seul INTEGER
.
Sur les types de données de petite taille, cette différence correspond probablement à pour cent de pourcentage du temps nécessaire pour récupérer des pages, des index de cheminement, des verrous de base de données, etc.
Et voici les chiffres (dans MySQL
):
CREATE TABLE aint (id INT NOT NULL PRIMARY KEY, value VARCHAR(100));
CREATE TABLE adouble (id1 INT NOT NULL, id2 INT NOT NULL, value VARCHAR(100), PRIMARY KEY (id1, id2));
CREATE TABLE bint (id INT NOT NULL PRIMARY KEY, aid INT NOT NULL);
CREATE TABLE bdouble (id INT NOT NULL PRIMARY KEY, aid1 INT NOT NULL, aid2 INT NOT NULL);
INSERT
INTO aint
SELECT id, RPAD('', FLOOR(RAND(20090804) * 100), '*')
FROM t_source;
INSERT
INTO bint
SELECT id, id
FROM aint;
INSERT
INTO adouble
SELECT id, id, value
FROM aint;
INSERT
INTO bdouble
SELECT id, id, id
FROM aint;
SELECT SUM(LENGTH(value))
FROM bint b
JOIN aint a
ON a.id = b.aid;
SELECT SUM(LENGTH(value))
FROM bdouble b
JOIN adouble a
ON (a.id1, a.id2) = (b.aid1, b.aid2);
t_source
est simplement une table factice avec 1 000 000
lignes.
aint
et unique
, bint
et bdouble
contiennent exactement les mêmes données, sauf que aint
a un entier sous la forme PRIMARY KEY
, tandis que adouble
contient une paire de deux entiers identiques.
Sur ma machine, les deux requêtes s'exécutent pendant 14,5 secondes, +/- 0,1 seconde
La différence de performance, le cas échéant, se situe dans la plage de fluctuations.