¿Es una buena práctica tener siempre una clave primaria de entero de autoincremento?

191

En mis bases de datos, tiendo a adquirir el hábito de tener una clave primaria de entero de incremento automático con el nombre idde cada tabla que hago para tener una búsqueda única para cualquier fila en particular.

¿Se considera esto una mala idea? ¿Hay algún inconveniente en hacerlo de esta manera? A veces tendré múltiples índices como id, profile_id, subscriptionsdónde idestá el identificador único, profile_idenlaces al exterior idde una Profiletabla, etc.

¿O hay escenarios en los que no desea agregar dicho campo?

AJJ
fuente
61
Eche un vistazo al problema del tanque alemán para ver un ejemplo en el que un identificador de incremento automático simple es un problema. Por supuesto, esto solo importa si está usando sus identificadores en público.
Bergi
24
@ArukaJ El punto es que filtra información sobre el sistema. Por ejemplo, suponga que la base de datos contiene publicaciones escritas por el usuario, cada una de las cuales obtiene una identificación secuencial. Digamos que haces cuatro publicaciones, cada una de las cuales obtiene una identificación: a las 4 a.m. (20), a las 5 a.m. (25), a las 8 p.m. (100) y a las 9 p.m. (200). Al mirar los identificadores, puede ver que solo se agregaron 5 publicaciones entre las 4 a.m. y las 5 a.m., mientras que 100 se agregaron entre las 8 p.m. y las 9 p.m. Si intentaba elegir el momento para un ataque de denegación de servicio, esa podría ser información valiosa.
Joshua Taylor
29
Para todos los que se quejan del "problema del tanque alemán" ... si lo único que impide que alguien acceda a los datos que no deberían es una clave en su URL ... tiene problemas mayores que GUID versus Auto INT.
Matthew Whited
11
@MatthewWhited No se trata solo de intercambiar parámetros en una URL. Supongamos que usa un sitio y crea el activo 100 a la vez t, y el activo 120 a la vez t + 60. Si puede ver ambas ID (100 y 120) en forma no ofuscada, ahora sabe la cantidad total de activos que existen, así como aproximadamente la tasa a la que se crean. Esta es la fuga de información. Esto no es puramente hipotético.
Chris Hayes
15
"¿Es una buena práctica siempre ..." No.
brian_o

Respuestas:

137

Nunca es una mala idea tener un identificador de fila único garantizado. Supongo que no debería decir nunca, pero vamos con la abrumadora mayoría del tiempo, es una buena idea.

Las desventajas potenciales teóricas incluyen un índice adicional para mantener y el espacio de almacenamiento adicional utilizado. Esa nunca ha sido una razón suficiente para no usar una.

Gran maestro B
fuente
11
Eso es lo que hago. La mayoría de las personas usan 'id' o 'tablename_id' (como user_id). El argumento generalmente no es si se necesita la columna, sino qué forma de nombrarla.
GrandmasterB
103
Personalmente, creo que el nombre de la tabla debería implicar el resto. TableName.ida diferencia de TableName.TableName_id, porque ¿a qué más se idreferirá eso ? Si tengo otro campo de identificación en la tabla, lo prefijo con un nombre de tabla si se refiere a otra tabla
AJJ
10
@ArukaJ mencionaste que estás usando SQLite. En realidad, es un caso un poco especial, ya que siempre hace que esa columna esté "debajo del capó". Por lo tanto, ni siquiera está utilizando ningún espacio adicional porque obtiene uno, lo quiera o no. Además, el rowid de SQLite siempre es un entero de 64 bits. Si entiendo que es correcto, si define una fila de incremento automático, será un alias para el rowid interno. ¡Así que es posible que siempre lo hayas hecho! Ver sqlite.org/autoinc.html
GrandmasterB
99
La única excepción que se me ocurre es si tiene un identificador único que se genera de otra manera, en cuyo caso esa debería ser la clave principal y una identificación de incremento automático es redundante.
HamHamJ
44
@GrandmasterB: La versión actual de SQLite permite crear WITHOUT ROWIDtablas (con un explícito PRIMARY KEY) como una optimización. Pero de lo contrario, una INTEGER PRIMARY KEYcolumna es un alias para el rowid.
dan04
92

No estoy de acuerdo con todas las respuestas anteriores. Hay muchas razones por las cuales es una mala idea agregar un campo de incremento automático en todas las tablas.

Si tiene una tabla donde no hay claves obvias, un campo de incremento automático parece una buena idea. Después de todo, no quieres select * from blog where body = '[10000 character string]'. Que prefieres select * from blog where id = 42. Yo diría que en la mayoría de estos casos, lo que realmente quieres es un identificador único; No es un identificador único secuencial. Probablemente desee utilizar un identificador universalmente único en su lugar.

Hay funciones en la mayoría de las bases de datos para generar identificadores únicos aleatorios ( uuiden mysql, postgres. newidEn mssql). Estos le permiten generar datos en múltiples bases de datos, en diferentes máquinas, en cualquier momento, sin conexión de red entre ellos, y aun así fusionar datos con cero conflictos. Esto le permite configurar más fácilmente múltiples servidores e incluso centros de datos, como por ejemplo, con microservicios.

Esto también evita que los atacantes adivinen las URL de las páginas a las que no deberían tener acceso. Si hay un https://example.com/user/1263probablemente también hay un https://example.com/user/1262. Esto podría permitir la automatización de una vulnerabilidad de seguridad en la página de perfil de usuario.

También hay muchos casos en los que una columna de líquido es inútil o incluso dañina. Digamos que tienes una red social. Hay una usersmesa y una friendsmesa. La tabla de amigos contiene dos columnas de ID de usuario y un campo de incremento automático. Desea 3ser amigo 5, así que inserte 3,5en la base de datos. La base de datos agrega una identificación de incremento automático y almacena 1,3,5. De alguna manera, el usuario 3vuelve a hacer clic en el botón "Agregar amigo". Si inserta 3,5nuevamente en la base de datos, la base de datos agrega una identificación de incremento automático e inserta 2,3,5. Pero ahora 3y 5son amigos entre sí dos veces! Eso es una pérdida de espacio, y si lo piensas, también lo es la columna de incremento automático. Todo lo que necesitas para ver si aybson amigos es seleccionar para la fila con esos dos valores. Son, juntos, un identificador de fila único. (Probablemente quieras escribir algo de lógica para asegurarte 3,5y que 5,3sean deduplicados).

Todavía hay casos en los que los ID secuenciales pueden ser útiles, como cuando se construye un acortador de url, pero en su mayoría (e incluso con el acortador de url) lo que realmente desea usar es un ID único generado aleatoriamente.

TL; DR: use UUID en lugar de incremento automático, si aún no tiene una forma única de identificar cada fila.

Filip Haglund
fuente
26
El problema con los UUID es que ocupan demasiado espacio para la mayoría de las tablas. Use el identificador único correcto para cada tabla.
Stephen
49
Todo el párrafo sobre la unicidad es discutible: la unicidad puede hacerse cumplir, con o sin una clave principal. Además, los UUID son mejores desde el punto de vista teórico, pero son terribles de usar al depurar / realizar tareas de DBA o hacer cualquier otra cosa que no sea "resistente a los ataques".
11
Otro escenario cuando los UUID son mejores: implementar una operación PUT idempotente, de modo que pueda volver a intentar las solicitudes de forma segura sin introducir filas duplicadas.
yurez
21
En el punto de "adivinar URL", tener una ID única (secuencial o de otro tipo) no implica exponer esa ID a los usuarios de la aplicación.
Dave Sherohman
77
Puramente desde el punto de vista de la base de datos, esta respuesta es completamente incorrecta. El uso de UUID en lugar de números enteros de incremento automático aumenta los índices demasiado rápido y afecta negativamente el rendimiento y el consumo de memoria. Si está hablando desde el punto de vista del servicio web o la aplicación web, de todos modos debería haber una capa entre la base de datos y el front end. Cualquier otra cosa es mal diseño. Usar los datos como clave principal es aún peor. Las claves primarias deben usarse solo en la capa de datos, en ningún otro lugar.
Drunken Code Monkey
60

Las claves autoincementales tienen principalmente ventajas.

Pero algunos posibles inconvenientes podrían ser:

  • Si tiene una clave comercial, también debe agregar un índice único en esa (s) columna (s) para hacer cumplir las reglas comerciales.
  • Al transferir datos entre dos bases de datos, especialmente cuando los datos están en más de una tabla (es decir, maestro / detalle), no es sencillo ya que las secuencias no se sincronizan entre las bases de datos, y primero deberá crear una tabla de equivalencia utilizando clave empresarial como una coincidencia para saber qué ID de la base de datos de origen corresponde con qué ID en la base de datos de destino. Sin embargo, eso no debería ser un problema al transferir datos de / a tablas aisladas.
  • Muchas empresas tienen herramientas de informes ad-hoc, gráficas, de apuntar y hacer clic, de arrastrar y soltar. Dado que las ID autoincrementales no tienen sentido, a este tipo de usuarios les resultará difícil entender los datos fuera de "la aplicación".
  • Si modifica accidentalmente la clave comercial, es probable que nunca recupere esa fila porque ya no tiene algo para que los humanos la identifiquen. Eso causó una falla en la plataforma BitCoin una vez .
  • Algunos diseñadores agregan una ID a una tabla de unión entre dos tablas, cuando la PK simplemente debe estar compuesta por las dos ID externas. Obviamente, si la tabla de unión está entre tres o más tablas, entonces tiene sentido una ID autoincremental, pero luego debe agregar una clave única cuando se aplica a la combinación de FK para hacer cumplir las reglas de negocio.

Aquí hay una sección de artículo de Wikipedia sobre las desventajas de las claves sustitutas.

Tulains Córdova
fuente
13
Culpar de la falla mt.gox a las claves sustitutas parece bastante dudoso. El problema era que incluían todos los campos en su clave compuesta, incluso los campos mutables / maleables.
CodesInChaos
66
Una desventaja "social" del uso de claves de incremento automático es que a veces "la empresa" supone que nunca debe haber huecos y exige saber qué sucedió con las filas que faltan cuando ocurre una inserción fallida (reversión de transacción).
Rick Ryker
44
Otra desventaja es que si el sistema crece tanto que tiene que fragmentar la base de datos, ya no puede usar el incremento automático para producir una clave global única. Cuando llegue a ese punto, es posible que tenga un montón de código que se basa en esa suposición. Hay otras formas de producir un identificador único que seguirá funcionando si la base de datos está fragmentada.
kasperd
1
@Voo No se garantiza que la base de datos elegida lo admita. Y tratar de implementarlo en una capa superior a la base de datos en sí misma significa que pierde algunas de las garantías que SQL le daría. Finalmente, cualquier asignación centralizada de ID aumentará la latencia si tiene un sistema distribuido.
Kasperd
1
@Voo Por supuesto, independientemente de la escala del sistema, uno no debería hacer demasiados supuestos sobre la naturaleza de las ID autoincrementadas. Si tiene una sola base de datos, se asignan en orden, pero no hay garantía de que se comprometan en orden. Y puede haber una brecha en la secuencia porque no todas las transacciones están comprometidas.
kasperd
20

Solo para ser contrario, no, NO necesita tener siempre una PK de AutoInc numérica.

Si analiza sus datos cuidadosamente, a menudo identifica claves naturales en los datos. Este suele ser el caso cuando los datos tienen un significado intrínseco para el negocio. A veces, las PK son artefactos de sistemas antiguos que los usuarios comerciales utilizan como segundo idioma para describir los atributos de su sistema. He visto los números VIN del vehículo utilizados como la clave principal de una tabla "Vehículo" en un sistema de gestión de flota, por ejemplo.

Sin embargo, se originó, SI ya tiene un identificador único, úselo. No cree una segunda clave primaria sin sentido; Es un desperdicio y puede causar errores.

A veces puede usar un AutoInc PK para generar un valor significativo para el cliente, por ejemplo, Números de política. Establecer el valor inicial en algo sensato y aplicar reglas comerciales sobre ceros a la izquierda, etc. Este es probablemente el enfoque de "lo mejor de ambos mundos".

Cuando tenga un pequeño número de valores que son relativamente estáticos, use valores que tengan sentido para el usuario del sistema. ¿Por qué usar 1,2,3 cuando puede usar L, C, H donde L, H y C representan vida, automóvil y hogar en un contexto de "Tipo de póliza" de seguro o, volviendo al ejemplo de VIN, qué tal usar "TO "para Toyota? Todos los autos Toyata tienen un VIN que comienza "TO". Es una cosa menos para que los usuarios recuerden, hace que sea menos probable que introduzcan errores de programación y de usuario e incluso puede ser un sustituto utilizable para una descripción completa en los informes de administración que simplifica los informes. para escribir y tal vez más rápido para generar.

Un desarrollo adicional de esto es probablemente "un puente demasiado lejos" y generalmente no lo recomiendo, pero lo estoy incluyendo por completo y puede que le resulte útil. Es decir, use la Descripción como clave principal. Para los datos que cambian rápidamente, esto es una abominación. Para datos muy estáticos que se informan en todo el tiempo , tal vez no. Solo lo menciono para que esté allí como una posibilidad.

SÍ uso AutoInc PK, solo engancho mi cerebro y busco mejores alternativas primero. El arte del diseño de bases de datos está haciendo algo significativo que se puede consultar rápidamente. Tener demasiadas uniones dificulta esto.

EDITAR Otro caso crucial en el que no necesita una PK autogenerada es el caso de las tablas que representan la intersección de otras dos tablas. Para seguir con la analogía del automóvil, un automóvil tiene 0..n accesorios, cada accesorio se puede encontrar en muchos automóviles. Para representar esto, debe crear una tabla Car_Accessory que contenga las PK de Car and Accessory y otra información relevante sobre las fechas del enlace, etc.

Lo que no necesita (generalmente) es un AutoInc PK en esta tabla: solo se podrá acceder a través del automóvil "dígame qué accesorios hay en este automóvil" o desde el Accesorio "dígales qué automóviles tienen este accesorio"

mcottle
fuente
44
> Todos los autos Toyata tienen un VIN que comienza "TO" Eso simplemente no es cierto. Comienzan con "JT" si se hace en Japón. Toyotas de fabricación estadounidense
Monty Harder
17
Don't create a second, meaningless primary key; it's wasteful and may cause errors.Sin embargo, si la forma de establecer la unicidad para un registro es una combinación de 6 columnas, entonces unirse a las 6 todo el tiempo es muy propenso a errores en sí mismo. Los datos, naturalmente, tienen un PK, pero es mejor usar una idcolumna y una restricción única en esas 6 columnas.
Brad
14
Admito que algunas de estas sugerencias me llevan un poco lejos. Sí, ser pragmático está bien, pero no puedo contar con qué frecuencia alguien juró la vida de su primogénito que algún atributo fuera del dominio seguirá siendo único por el resto de los días. Bueno, por lo general eso funcionó bien hasta la segunda semana después de salir en vivo, cuando aparecieron los primeros duplicados. ;) Usar una "descripción" como PK está muy lejos.
AnoE
2
@ Monty, mi mal, tienes razón. Memoria falible, han pasado 20 años desde que diseñé los sistemas de gestión de flotas. No, el VIN no era la clave principal :) Usé un AutoInc Asset_ID IIRC que conduce a algo que olvidé. Tablas que son los enlazadores para las relaciones de muchos a muchos donde se vincula, por ejemplo, automóvil a accesorio (por ejemplo, techo solar) Muchos automóviles tienen muchos accesorios, por lo que necesita una tabla "Car_Accessory" que contiene Car_ID y Accessory_ID pero NO necesita Car_Accesory_ID como una PK de AutoInc.
mcottle
77
Es realmente sorprendente lo pocas que hay realmente "llaves naturales" inmutables. SSN? No, pueden cambiar. Es raro, pero puede suceder. Nombres de usuario? No. Eventualmente, alguien tendrá una razón comercial válida para cambiar. VIN es a menudo un ejemplo de libro de texto, pero no hay muchos otros. Incluso las direcciones de casa pueden cambiar, dados los cambios de nombres de calles.
Erik Funkenbusch
12

Muchas tablas ya tienen una identificación única natural. No agregue otra columna de identificación única (incremento automático o de otro modo) en estas tablas. Utilice la identificación única natural en su lugar. Si agrega otra identificación única, esencialmente tiene una redundancia (duplicación o dependencia) en sus datos. Esto va en contra de los principios de normalización. Una identificación única depende de la otra para la precisión. Esto significa que deben mantenerse perfectamente sincronizados en todo momento en cada sistema que gestione estas filas. Es solo otra fragilidad en la integridad de sus datos que realmente no desea tener que administrar y validar a largo plazo.

La mayoría de las tablas en estos días realmente no necesitan el impulso de rendimiento muy menor que daría una columna de identificación única adicional (y a veces incluso perjudica el rendimiento). Como regla general en TI, ¡evite la redundancia como la peste! Resístalo en todas partes que te sugieran. Es anatema. Y preste atención a la cita. Todo debe ser lo más simple posible, pero no más simple. No tenga dos identificadores únicos donde uno sea suficiente, incluso si el natural parece menos ordenado.

Brad Thomas
fuente
3
¿No debería usar IDs "naturales" como claves principales si está absolutamente garantizado que nunca cambiarán? Por ejemplo, no debe usar un número de licencia de conducir como clave principal, porque si una persona obtiene una nueva licencia de conducir, ¡necesitará actualizar no solo esa tabla, sino también cualquier tabla con claves extranjeras que hagan referencia a ella!
ekolis
1
Hay varias razones por las cuales el número de licencia de conducir no califica como una identificación única natural. En primer lugar, algunos de ellos se derivan de otros datos, como la fecha de nacimiento y el nombre. No se garantiza que sean únicos en todos los estados. Y para tomar su ejemplo, cuando una persona vuelve a emitir una licencia con el mismo número, pero tal vez una caducidad extendida, ¿qué sucede entonces? Tienen una licencia diferente con el mismo número. Una identificación natural todavía tiene que cumplir con las propiedades básicas de una clave primaria. El número de licencia de conducir (al menos en los EE. UU.) Tiene algunas deficiencias en este sentido.
Brad Thomas
1
OK, supongo que entonces entendí mal la definición de identificación natural; Pensé que era simplemente una identificación definida por las reglas comerciales, independientemente de si realmente se garantiza que sea inmutable.
ekolis
10

En sistemas más grandes, la ID es un refuerzo de consistencia, úselo casi en cualquier lugar. En este contexto, NO se recomiendan las claves primarias individuales, son caras en el resultado final (lea por qué).

Cada regla tiene una excepción, por lo que es posible que no necesite un ID de incremento automático de enteros en las tablas de preparación utilizadas para la exportación / importación y en tablas similares de un solo sentido o tablas temporales. También preferiría GUID en lugar de ID en sistemas distribuidos.

Muchas respuestas aquí sugieren que se debe tomar una clave única existente. Bueno, incluso si tiene 150 caracteres? No lo creo.

Ahora mi punto principal:

Parece que los opositores de la identificación de entero de incremento automático están hablando de pequeñas bases de datos con hasta 20 tablas. Allí pueden permitirse un enfoque individual para cada mesa.

PERO una vez que tenga un ERP con más de 400 tablas, tener una ID de incremento automático en cualquier lugar (excepto los casos mencionados anteriormente) tiene mucho sentido. No confíe en otros campos únicos, incluso si están presentes y asegurados para ser únicos.

  • Se beneficia de una convención universal que ahorra tiempo, esfuerzo y fácil de recordar.
  • En la mayoría de los casos, las JOINtablas, sin necesidad de verificar cuáles son las claves.
  • Puede tener rutinas de código universal trabajando con su columna de autoincremento entero.
  • Puede ampliar su sistema con nuevas tablas o complementos de usuario no previstos anteriormente simplemente consultando las ID de las tablas existentes. Ya están allí desde el principio, sin costo adicional para agregarlos adicionalmente.

En sistemas más grandes, puede valer la pena ignorar los beneficios menores de esas claves primarias individuales y usar de manera consistente la identificación de incremento automático en la mayoría de los casos. El uso de campos únicos existentes como claves primarias puede estar ahorrando algunos bytes por registro, pero el almacenamiento adicional o el tiempo de indexación no plantean ningún problema en los motores de bases de datos actuales. En realidad, está perdiendo mucho más dinero y recursos en el tiempo perdido de los desarrolladores / mantenedores. El software actual debe optimizarse para el tiempo y el esfuerzo de los programadores: qué enfoque con identificaciones consistentes cumple mucho mejor.

miroxlav
fuente
Por experiencia personal, estoy totalmente de acuerdo con la segunda mitad de su respuesta. Necesitará claves únicas a nivel mundial con mucha menos frecuencia de lo que necesitará índices rápidos y compactos. Si necesita uno, cree una tabla GlobalEntities con una ID autogenerada y una columna UUID. A continuación, agregue una clave externa ExGlobalEntityId a la tabla Clientes, por ejemplo. O use un hash de algunos de los valores.
Drunken Code Monkey
8

No es una buena práctica hacer diseños superfluos. Es decir, no es una buena práctica tener siempre una clave primaria de incremento automático int cuando no se necesita.

Veamos un ejemplo donde no se necesita uno.

Tiene una tabla para artículos: tiene una clave primaria int idy una columna varchar denominada title.

También tiene una tabla llena de categorías de artículos: idclave primaria int, varchar name.

Una fila en la tabla Artículos tiene un id5 y un title "Cómo cocinar ganso con mantequilla". Desea vincular ese artículo con las siguientes filas en su tabla de categorías: "Fowl" ( id : 20), "Goose" ( id : 12), "Cooking" ( id : 2), "Butter" (id: 9) .

Ahora tiene 2 tablas: artículos y categorías. ¿Cómo se crea la relación entre los dos?

Podría tener una tabla con 3 columnas: id (clave principal), article_id (clave externa), category_id (clave externa). Pero ahora tienes algo como:

El | id | a_id | c_id |
El | 1 | 5 | 20 |
El | 2 | 5 | 12 |
El | 3 | 5 | 2 |

Una mejor solución es tener una clave primaria que se compone de 2 columnas.

El | a_id | c_id |
El | 5 | 20 |
El | 5 | 12 |
El | 5 | 2 |

Esto se puede lograr haciendo:

create table articles_categories (
  article_id bigint,
  category_id bigint,
  primary key (article_id, category_id)
) engine=InnoDB;

Otra razón para no utilizar un entero de incremento automático es si está utilizando UUID para su clave principal.

Los UUID son, por definición, únicos, lo que logra lo mismo que usar enteros únicos. También tienen sus propios beneficios adicionales (y contras) sobre los enteros. Por ejemplo, con un UUID, usted sabe que la cadena única a la que se refiere apunta a un registro de datos en particular; Esto es útil en los casos en que no tiene 1 base de datos central, o donde las aplicaciones tienen la capacidad de crear registros de datos fuera de línea (luego subirlos a la base de datos en una fecha posterior).

Al final, no debes pensar en las claves primarias como una cosa. Debe pensar en ellos como la función que realizan. ¿Por qué necesitas claves primarias? Para poder identificar de forma exclusiva conjuntos específicos de datos de una tabla utilizando un campo que no se cambiará en el futuro. ¿Necesita una columna en particular llamada idpara hacer esto, o puede basar esta identificación única en otros datos (inmutables)?

anw
fuente
7

¿O hay escenarios en los que no desea agregar dicho campo?

Seguro.

En primer lugar, hay bases de datos que no tienen aumentos automáticos (por ejemplo, Oracle, que ciertamente no es uno de los contendientes más pequeños). Esto debería ser una primera indicación de que no a todos les gustan o los necesitan.

Más importante, piense en qué es realmente la ID : es una clave principal para sus datos. Si tiene una tabla con una clave primaria diferente, entonces no necesita una ID y no debe tenerla. Por ejemplo, una tabla (EMPLOYEE_ID, TEAM_ID)(donde cada empleado puede estar en varios equipos al mismo tiempo) tiene una clave primaria claramente definida que consiste en esas dos ID. Agregar una IDcolumna de autoincremento , que también es una clave principal para esta tabla, no tendría ningún sentido. Ahora está cargando 2 claves primarias, y la primera palabra en "clave primaria" debería darle una pista de que realmente debería tener solo una.

AnoE
fuente
99
(No es un usuario de Oracle, así que perdone la pregunta, pero) ¿Oracle no utiliza la secuencia de la misma manera que otros usan Autoincrement / Identity? ¿Está diciendo que Oracle no tiene un tipo de datos de Autoincremento realmente solo un argumento semántico?
Brad
Bueno, eso fue solo un pequeño punto; la parte principal es que una ID en ejecución no es apropiada para cada tabla, por lo tanto, acostumbrarse a simplemente colocar una ID automática en cada tabla puede no ser la más inteligente.
AnoE
no hay dos claves principales, solo hay una clave principal y todos los demás se denominan claves candidatas si pueden servir también como claves primarias ..
rahul tyagi
7

Usualmente uso una columna de "identidad" (entero de incremento automático) cuando defino nuevas tablas para datos "de larga duración" (registros que espero insertar una vez y mantener indefinidamente incluso si terminan "eliminados lógicamente" configurando un campo de bits )

Hay algunas situaciones en las que puedo pensar cuando no desea usarlas, la mayoría de las cuales se reducen a escenarios en los que una tabla en una instancia de la base de datos no puede ser la fuente autorizada para los nuevos valores de ID:

  • Cuando las ID incrementales serían demasiada información para un atacante potencial. El uso de una columna de identidad para servicios de datos "públicos" lo hace vulnerable al "problema del tanque alemán"; si existe la identificación de registro 10234, es lógico que exista el registro 10233, 10232, etc., al menos hasta el registro 10001, y luego es fácil verificar el registro 1001, 101 y 1 para averiguar dónde comenzó su columna de identidad. Los GUID V4 compuestos principalmente de datos aleatorios rompen este comportamiento incremental por diseño, de modo que solo porque existe un GUID, un GUID creado al aumentar o disminuir un byte del GUID no existe necesariamente, lo que hace que sea más difícil para un atacante usar un servicio de forma intencionada para la recuperación de un solo registro como herramienta de volcado. Existen otras medidas de seguridad que pueden restringir mejor el acceso, pero esto ayuda.
  • En M: M tablas de referencias cruzadas. Este es un tipo de dame, pero lo he visto antes. Si tiene una relación de muchos a muchos entre dos tablas en su base de datos, la solución de referencia es una tabla de referencia cruzada que contiene columnas de clave externa que hacen referencia a la PK de cada tabla. La PK de esta tabla debería ser prácticamente siempre una clave compuesta de las dos claves externas, para obtener el comportamiento del índice incorporado y garantizar la unicidad de las referencias.
  • Cuando planeas insertar y eliminar de forma masiva en esta tabla mucho. Probablemente la mayor desventaja de las columnas de identidad es el alboroto adicional que debe atravesar cuando inserta filas de otra tabla o consulta, donde desea mantener los valores clave de la tabla original. Debe activar la "inserción de identidad" (sin embargo, eso se hace en su DBMS), luego asegurarse manualmente de que las claves que está insertando son únicas, y luego, cuando haya terminado con la importación, debe configurar el contador de identidad en el metadatos de la tabla al valor máximo presente. Si esta operación ocurre mucho en esta tabla, considere un esquema PK diferente.
  • Para mesas distribuidas.Las columnas de identidad funcionan muy bien para bases de datos de instancia única, pares de conmutación por error y otros escenarios en los que una instancia de base de datos es la única autoridad en todo el esquema de datos en un momento dado. Sin embargo, hay tan grande que puedes ir y todavía tener una computadora lo suficientemente rápida. La replicación o el envío del registro de transacciones puede obtener copias adicionales de solo lectura, pero también existe un límite en la escala de esa solución. Tarde o temprano necesitará dos o más instancias de servidor que manejen las inserciones de datos y luego se sincronicen entre sí. Cuando se presente esa situación, querrá un campo GUID en lugar de uno incremental, porque la mayoría de los DBMS vienen preconfigurados para usar una parte de los GUID que generan como un identificador específico de la instancia, y luego generan el resto del identificador al azar o incrementalmente. En cualquier caso,
  • Cuando tiene que imponer la unicidad en varias tablas en la base de datos.Es común en los sistemas de contabilidad, por ejemplo, administrar el Libro mayor (con una fila para cada crédito o débito de cada cuenta que haya ocurrido, por lo que se vuelve muy grande muy rápidamente) como una secuencia de tablas, cada una representando un mes calendario / año. Las vistas se pueden crear para unirlas para generar informes. Lógicamente, esta es una tabla muy grande, pero cortarla facilita los trabajos de mantenimiento de la base de datos. Sin embargo, presenta el problema de cómo administrar las inserciones en varias tablas (lo que le permite comenzar a registrar transacciones en el próximo mes sin cerrar el último) sin terminar con claves duplicadas. Nuevamente, los GUID en lugar de las columnas de enteros de identidad son la solución, ya que el DBMS está diseñado para generarlos de una manera verdaderamente única,

Hay soluciones alternativas que permiten el uso de columnas de identidad en estas situaciones, como he mencionado anteriormente, pero en la mayoría de ellas, la actualización de la columna de enteros de identidad a un GUID es más simple y resuelve el problema más completamente.

KeithS
fuente
1
Hay casos en los que aún puede necesitar ID en las tablas M: N (usando columnas ID, ID_M, ID_N) debido a que adjunta propiedades a instancias de su relación M: N.
miroxlav
No se garantiza que V4 GUIDS use un PNRG criptográficamente fuerte, por lo que realmente no debe confiar en él para su primer ejemplo imo (aunque si su motor db hace promesas más fuertes, podría estar bien, pero eso no es portátil). De lo contrario, una publicación bien razonada.
Voo
1
@miroxlav: afirmaría que si una tabla tiene suficientes metadatos adicionales con respecto a la relación de que una PK separada fuera de los dos FK es una buena idea, ya no es realmente una tabla de referencia cruzada; es su propia entidad la que hace referencia a las otras dos.
KeithS
@Voo: tienes razón, no se garantiza que los GUID V4 sean criptográficamente aleatorios, solo únicos (como todos los GUID lo son). Sin embargo, los números de cola de los aviones de combate estadounidenses tampoco se generan a partir de datos / algoritmos de semillas criptográficamente aleatorios. Lo que realmente está buscando es un dominio escasamente poblado; un GUID V4 tiene 112 bytes de datos aleatorios, capaces de identificar de forma exclusiva los registros 5e33.
KeithS
Para poner ese número en perspectiva, cada hombre, mujer y niño en el planeta (los 7 mil millones) podría tener 741 billones de puntos de datos catalogados e identificados individualmente en nuestra base de datos, y aún estaríamos usando un valor GUID por mil millones disponibles. Big Data, como industria global, ni siquiera está cerca de esta escala de conocimiento. Incluso dado un patrón para la generación de GUID, hay otras fuentes de entropía involucradas, como el orden en que los datos ingresan al sistema y se les asigna un GUID.
KeithS
7

Una clave primaria con incremento automático (identidad) es una buena idea, excepto para observar que no tiene sentido fuera del contexto de la base de datos y los clientes inmediatos de esa base de datos. Por ejemplo, si transfiere y almacena algunos de los datos en otra base de datos, luego proceda a escribir datos diferentes en ambas tablas de la base de datos, los identificadores divergirán, es decir, los datos con un identificador de 42 en una base de datos no necesariamente coincidirán con los datos con un id de 42 en el otro.

Dado esto, si es necesario poder identificar filas de manera exclusiva fuera de la base de datos (y con frecuencia lo es), entonces debe tener una clave diferente para este propósito. Una clave comercial cuidadosamente seleccionada servirá, pero a menudo terminará en una posición de una gran cantidad de columnas necesarias para garantizar la unicidad. Otra técnica es tener una columna Id como una clave primaria agrupada de incremento automático y otra columna de identificador único (guid) como una clave única no agrupada, con el fin de identificar de forma única la fila donde sea que exista en el mundo. La razón por la que todavía tiene una clave de incremento automático en este caso es porque es más eficiente agrupar e indexar la clave de incremento automático que hacer lo mismo con un guid.

Un caso en el que es posible que no desee una clave de incremento automático sería una tabla de muchos a muchos, donde la clave principal es un compuesto de las columnas Id de otras dos tablas (aún podría tener una clave de incremento automático aquí, pero yo no veo el punto de eso).

Otra pregunta es el tipo de datos de la clave de incremento automático. El uso de un Int32 le brinda un rango de valores grande pero relativamente limitado. Personalmente, con frecuencia utilizo columnas bigint para el Id, para prácticamente nunca tener que preocuparme por la falta de valores.

MatthewToday
fuente
6

Como otras personas han defendido una clave principal incremental, crearé una para un GUID:

  • Se garantiza que sea único.
  • Puede hacer un viaje menos a la base de datos para obtener datos en su aplicación. (Para una tabla de tipos, por ejemplo, puede almacenar el GUID en la aplicación y usarlo para recuperar el registro. Si usa una identidad, debe consultar la base de datos por nombre y he visto muchas aplicaciones que hacen esto para obtener el PK y luego lo consulta nuevamente para obtener todos los detalles).
  • Es útil para ocultar datos. www.domain.com/Article/2 Me deja saber que solo tiene dos artículos, mientras que www.domain.com/article/b08a91c5-67fc-449f-8a50-ffdf2403444a no me dice nada.
  • Puede combinar registros de diferentes bases de datos fácilmente.
  • MSFT usa GUID para la identidad.

Editar: punto duplicado

Lógica de tres valores
fuente
55
-1. No se garantiza que un GUID / UUID sea único, y no es 100% único. Un GUID todavía tiene una longitud finita, por lo que en algún momento puede arriesgarse a obtener un duplicado, aunque es muy poco probable. Su punto sobre menos viajes a la base de datos tampoco es válido: ¿por qué no puede almacenar la identificación primaria en la aplicación, como puede hacerlo con la clave GUID?
Niklas H
2
Jeff Atwood lo dice mucho mejor de lo que yo podría. blog.codinghorror.com/primary-keys-ids-versus-guids
Three Value Logic
En cuanto a por qué no puedes almacenar la identificación primaria en tu aplicación. Porque la base de datos lo crea. Si ejecuta sus semillas en una base de datos vacía, puede suponer que la ID será 1. ¿Qué sucede si ejecuta el mismo script en una base de datos con datos? La identificación no será 1.
Three Value Logic
No dijo nada sobre la creación de ID en la aplicación, simplemente escribió "almacenar". Pero si es necesario crear la ID fuera de la base de datos, entonces sí, un GUID podría ser la respuesta.
Niklas H
2
Yo agregaría que escalan mejor. Las bases de datos Big Data NoSQL como Cassandra ni siquiera admiten claves de incremento automático.
Karl Bielefeldt
2

Como principio del buen diseño, cada tabla debe tener una forma confiable de identificar de forma única una fila. Aunque para eso sirve una clave primaria, no siempre requiere la existencia de una clave primaria. Agregar una clave primaria a cada tabla no es una mala práctica ya que proporciona una identificación de fila única, pero puede ser innecesaria.

Para mantener relaciones confiables entre las filas de dos o más tablas, debe hacerlo mediante claves externas, de ahí la necesidad de claves primarias en al menos algunas tablas. Agregar una clave principal a cada tabla hace que sea más fácil extender el diseño de su base de datos cuando llega el momento de agregar nuevas tablas o relaciones a los datos existentes. Planear con anticipación siempre es algo bueno.

Como principio básico (quizás una regla difícil), el valor de una clave primaria nunca debería cambiar durante la vida de su fila. Es aconsejable suponer que cualquier dato comercial en una fila está sujeto a cambios a lo largo de su vida útil, por lo que cualquier dato comercial será un mal candidato para una clave primaria. Esta es la razón por la cual algo abstracto como un entero auto-incrementado es a menudo una buena idea. Sin embargo, los enteros autoincrementados tienen sus limitaciones.

Si sus datos solo tendrán vida dentro de su base de datos, los enteros con incremento automático están bien. Pero, como se ha mencionado en otras respuestas, si alguna vez desea que sus datos se compartan, se sincronicen o tengan una vida fuera de su base de datos, los enteros con incremento automático son claves primarias deficientes. Una mejor opción será un guid (también conocido como uuid "id universalmente único").

Zenilogix
fuente
2

La pregunta, y muchas de las respuestas, pierden el punto importante de que todas las claves naturales para cada tabla residen únicamente en el esquema lógico de la base de datos, y todas las claves sustitutas para cada tabla residen únicamente en el esquema físico de la base de datos. otras respuestas analizan únicamente los beneficios relativos de las claves sustitutas de enteros frente a GUID, sin analizar las razones por las cuales las claves sustitutas se utilizan correctamente y cuándo.

Por cierto: evitemos el uso del término mal definido e impreciso clave principal . Es un artefacto de modelos de datos pre-relacionales que primero fue cooptado (imprudentemente) en el modelo relacional, y luego cooptados nuevamente en el dominio físico por varios proveedores de RDBMS. Su uso solo sirve para confundir la semántica.

Tenga en cuenta del modelo relacional que, para que el esquema lógico de la base de datos esté en la primera forma normal , cada tabla debe tener un conjunto de campos visibles para el usuario, conocido como clave natural, que identifique de forma única cada fila de la tabla. En la mayoría de los casos, esta clave natural se identifica fácilmente, pero en ocasiones se debe construir, ya sea como un campo de desempate o de otra manera. Sin embargo, dicha clave construida siempre es visible para el usuario y, por lo tanto, siempre reside en el esquema lógico de la base de datos.

Por el contrario, cualquier clave sustituta en una tabla reside únicamente en el esquema físico de la base de datos (y, por lo tanto, siempre debe ser completamente invisible para los usuarios de la base de datos, por razones de seguridad y para el mantenimiento de la integridad de la base de datos). La única razón para introducir una clave sustituta es abordar los problemas de rendimiento en el mantenimiento físico y el uso de la base de datos; ya sean uniones, replicación, múltiples fuentes de hardware para datos u otros.

Dado que la única razón para la introducción de una clave sustituta es el rendimiento, supongamos que deseamos que sea eficaz. Si el problema de rendimiento en cuestión son las uniones, entonces necesariamente deseamos que nuestra clave sustituta sea lo más estrecha posible (sin interferir en el hardware, por lo que generalmente se eliminan los enteros cortos y los bytes). El rendimiento de la unión depende de una altura de índice mínima, por lo que un entero de 4 bytes es una solución natural. Si su problema de rendimiento es la tasa de inserción, un entero de 4 bytes también puede ser una solución natural (dependiendo de las partes internas de su RDBMS). Si su problema de rendimiento para una tabla es la replicación o múltiples fuentes de datos que alguna otra tecnología de clave sustituta , ya sea un GUID o una clave de dos partes (Host ID + entero) puede ser más adecuado. No soy personalmente un favorito de los GUID, pero son convenientes.

En resumen, no todas las tablas requerirán una clave sustituta (de ningún tipo); solo deben usarse cuando se considere necesario para el desempeño de la tabla en consideración. Independientemente de la tecnología de clave sustituta común que prefiera, piense detenidamente sobre las necesidades reales de la tabla antes de elegir; Cambiar la opción de tecnología clave sustituta para una mesa será un trabajo agotador. Documente la métrica clave de rendimiento de su tabla para que sus sucesores comprendan las elecciones realizadas.

Casos especiales

  1. Si los requisitos de su negocio exigen una numeración secuencial de transacciones para fines de auditoría (u otros) que ese campo no es una clave sustituta; Es una clave natural (con requisitos adicionales). De la documentación, un entero de incremento automático solo genera claves sustitutas , por lo tanto, busque otro mecanismo para generarlo. Obviamente, será necesario algún tipo de monitor, y si obtiene sus transacciones de varios sitios, entonces un sitio será especial , en virtud de ser el sitio host designado para el monitor.

  2. Si su tabla nunca tendrá más de cien filas, la altura del índice es irrelevante; cada acceso se realizará mediante un escaneo de tabla. Sin embargo, las comparaciones de cadenas en cadenas largas seguirán siendo mucho más caras que la comparación de un entero de 4 bytes, y más caras que la comparación de un GUID.

  3. Una tabla de valores de código tecleados por un campo de código char (4) debe tener el mismo rendimiento que uno con un entero de 4 bytes. Aunque no tengo pruebas de esto, uso el supuesto con frecuencia y nunca he tenido motivos para lamentarlo.

Pieter Geerkens
fuente
-1

No solo no es una buena práctica, de hecho, se describe como un antipatrón en el libro SQL Antipatterns de Bill Karwin.

No todas las tablas necesitan una pseudoclave, una clave primaria con un valor arbitrario, no algo que tenga un valor semántico para el modelo, y no hay razón para llamarla siempre id.

Pedro Werneck
fuente
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en las respuestas anteriores 9
mosquito
2
¿Y por qué esto podría ser importante?
mosquito
3
@gnat Porque es un libro sobre mejores prácticas, que aborda directamente la pregunta. ¿No es obvio?
Pedro Werneck
3
No es lo más mínimo. Google búsqueda "reserva sql mejores prácticas" muestra unos 900K enlaces a mí, ¿por qué éste es particularmente digna
mosquito
1
@gnat No voy a discutir todo el día. No te gusta la respuesta, para eso están los votos negativos.
Pedro Werneck
-2

Esto es bastante universal; de lo contrario, deberá validar que la clave sea realmente única. Esto se haría mirando todas las otras claves ... lo que llevaría mucho tiempo. Tener una clave incremental se vuelve costoso a medida que su número de registro se acerca al valor de desbordamiento de la clave.

Por lo general, hago que los punteros sean nombres de campo más obvios, como ref_{table}idea similar o similar.

Si no es necesario señalar externamente un registro, entonces no necesita una identificación.

Johnny V
fuente
¿Valor de rollover clave?
AJJ
Un número entero sin signo tiene un valor máximo de 4294967295 antes de agregar 1 lo transferirá a 0. Recuerde que si agrega un registro y luego lo elimina, el contador aún aumenta. Asegúrese de usarlo unsigned intpara el tipo de campo; de lo contrario, el límite es la mitad de ese número.
Johnny V
Desbordamiento de enteros - en.wikipedia.org/wiki/Integer_overflow
Johnny V
2
Si agrega / elimina muchas filas, el contador de incremento automático se desbordará eventualmente.
Johnny V
1
¿Cómo manejan las personas el rollover? ¿Qué sucede si hay registros con una ID baja que nunca se eliminan, pero está comenzando a acercarse al final donde algunas ID están en el extremo superior de 4294967295? ¿Se puede hacer una "reindexación"?
AJJ
-2

No diría que siempre se debe hacer. Tengo una tabla aquí sin una clave única, y no necesita una. Es un registro de auditoría. Nunca habrá una actualización, las consultas devolverán todos los cambios a lo que se está registrando, pero eso es lo mejor que se puede hacer razonablemente, se necesita un humano para definir un cambio erróneo. (¡Si el código pudiera, lo habría rechazado en primer lugar!)

Loren Pechtel
fuente
-3

Un contador de incremento automático para una clave primaria no es una buena idea. Esto se debe a que debe volver a la base de datos para encontrar la siguiente clave e incrementarla en una antes de insertar sus datos.

Dicho esto, generalmente usaría lo que la base de datos pueda proporcionar para la clave primaria en lugar de tenerla como parte de la aplicación.

Al permitir que la base de datos se la proporcione de forma nativa, puede garantizar que la clave sea única para lo que necesita.

Por supuesto, no todas las bases de datos lo admiten. En cuyo caso, generalmente uso una tabla que almacena depósitos de claves y uso rangos altos y bajos que se administran en la aplicación. Esta es la solución más eficaz que encuentro porque obtienes un rango de 10000 números y los autoincrementas en la instancia de la aplicación. Otra instancia de aplicación puede recoger otro grupo de números para trabajar. Necesita una primitiva de clave primaria suficientemente grande, como una longitud de 64 bits.

UUID que no uso como claves primarias porque el costo de construirlos y almacenarlos es mucho mayor que incrementar un valor largo en uno. Los UUID todavía se ocupan de la paradoja del cumpleaños en que teóricamente puede surgir un duplicado.

Arquímedes Trajano
fuente
3
No. las claves de incremento automático significan que la base de datos realiza automáticamente el incremento de la clave. A veces (¡te estoy mirando, Oracle!) Necesitas una combinación de secuencia + disparador para hacerlo, pero nunca necesitas buscar el valor insertado previamente para la clave, agregar 1 y luego usarlo.
SQB
Con algunos marcos de persistencia, como JPA, si desea devolver el valor de la clave que se creó a la persona que llama, debe cargar el registro para ver la clave.
Arquímedes Trajano