clave sustituta vs natural: ¿números concretos sobre diferencias de rendimiento?
-
22-07-2019 - |
Pregunta
Existe un debate saludable entre claves sustitutas y naturales:
Mi opinión, que parece estar en línea con la mayoría (es una mayoría delgada), es que debe usar claves sustitutas a menos que una clave natural sea completamente obvia y garantice que no cambie. Entonces debes imponer la unicidad en la clave natural. Lo que significa claves sustitutas casi todo el tiempo.
Ejemplo de los dos enfoques, comenzando con una tabla de empresa:
1: clave sustituta: la tabla tiene un campo de ID que es el PK (y una identidad). Los nombres de las empresas deben ser únicos por estado, por lo que existe una restricción única allí.
2: Clave natural: la tabla usa CompanyName y State como PK; satisface tanto la PK como la unicidad.
Digamos que la PK de la empresa se usa en otras 10 tablas. Mi hipótesis, sin números que lo respalden, es que el enfoque de clave sustituta sería mucho más rápido aquí.
El único argumento convincente que he visto para la clave natural es para una tabla de muchos a muchos que usa las dos claves externas como clave natural. Creo que en ese caso tiene sentido. Pero puede meterse en problemas si necesita refactorizar; eso está fuera del alcance de esta publicación, creo.
¿Alguien ha visto un artículo que compara las diferencias de rendimiento en un conjunto de tablas que usan claves sustitutas vs. el mismo conjunto de tablas usando claves naturales ? Mirar alrededor de SO y Google no ha producido nada que valga la pena, solo una gran cantidad de elaboración de teorías.
Actualización importante : he comenzado a crear un conjunto de tablas de prueba que responden a esta pregunta. Se ve así:
- PartNatural - tabla de piezas que usa el PartNumber único como PK
- PartSurrogate - tabla de piezas que usa una ID (int, identidad) como PK y tiene un índice único en PartNumber
- Planta - ID (int, identidad) como PK
- Ingeniero - ID (int, identidad) como PK
Cada parte se une a una planta y cada instancia de una parte en una planta se une a un ingeniero. Si alguien tiene un problema con este banco de pruebas, ahora es el momento.
Solución
¡Usa ambos! Las claves naturales evitan la corrupción de la base de datos (la inconsistencia podría ser una mejor palabra). Cuando el " derecho " la clave natural, (para eliminar filas duplicadas) funcionaría mal debido a la longitud o al número de columnas involucradas, para fines de rendimiento, se puede agregar una clave sustituta y usarla como claves foráneas en otras tablas en lugar de la clave natural. Pero la clave natural debe permanecer como una clave alternativa o índice único para evitar la corrupción de datos y garantizar la coherencia de la base de datos ...
Gran parte del hoohah (en el " debate " sobre este tema), puede deberse a lo que es una suposición falsa: que debe usar la Clave principal para combinaciones y claves foráneas en otras tablas. ESTO ES FALSO Puede usar CUALQUIER clave como destino para claves externas en otras tablas. Puede ser la clave primaria, una clave alternativa o cualquier índice único o restricción única. Y en cuanto a las uniones, puede usar cualquier cosa para una condición de unión, ¡ni siquiera tiene que ser una clave, un idex o incluso único! (aunque si no es único, obtendrá varias filas en el producto cartesiano que crea).
Otros consejos
Las claves naturales difieren de las claves sustitutas en valor, no tipo.
Cualquier tipo puede usarse para una clave sustituta, como un VARCHAR
para el slug
generado por el sistema o algo más.
Sin embargo, los tipos más utilizados para las claves sustitutas son INTEGER
y RAW (16)
(o cualquier tipo que utilice su RDBMS
para GUID
's),
Comparar enteros sustitutos y enteros naturales (como SSN
) lleva exactamente el mismo tiempo.
La comparación de VARCHAR
s toma en cuenta la intercalación y generalmente son más largos que los enteros, lo que los hace menos eficientes.
Comparar un conjunto de dos INTEGER
es probablemente también menos eficiente que comparar un solo INTEGER
.
En los tipos de datos de tamaño pequeño, esta diferencia es probablemente porcentajes de porcentajes del tiempo requerido para recuperar páginas, recorrer índices, trabar bases de datos, etc.
Y aquí están los números (en 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
es solo una tabla ficticia con 1,000,000
filas.
aint
y adouble
, bint
y bdouble
contienen exactamente los mismos datos, excepto que aint
tiene un entero como PRIMARY KEY
, mientras que adouble
tiene un par de dos enteros idénticos.
En mi máquina, ambas consultas se ejecutan durante 14.5 segundos, +/- 0.1 segundos
La diferencia de rendimiento, si la hay, está dentro del rango de fluctuaciones.