SQLA veces tengo la impresión de que elegir las columnas que constituirán la clave primaria de una tabla es una decisión que suele tomarse muy a la ligera, cuando la realidad es que la correcta elección de las claves primarias es un factor decisivo para poder construir un modelo relacional de base de datos bien formado, coherente, mantenible y fácilmente ampliable. Lo más irónico del asunto es que en realidad resulta una tarea muy sencilla si se comprende que ningún valor tomado del dominio que estamos intentando modelar sirve para identificar unívocamente a los registros en base de datos. Dicho de otra forma, no puede utilizarse como clave primaria una columna que contenga información que tenga significado para los usuarios; hay que crear una columna expresamente para la clave primaria. O dicho de una tercera forma, si tiene que crear una tabla de personas no utilice DNI como clave primaria.

¿Pero por qué no es una buena idea elegir DNI como clave primaria?, ¿acaso ese número no nos identifica de forma unívoca?. Pues sí, pero rotundamente NO. Piense por ejemplo que los menores de edad o los ciudadanos de otros paises no tienen normalmente un carnet de identidad, y todo ello sin mencionar además el hecho de que los números del DNI no son únicos, históricamente la misma numeración se ha reutilizado en distintas personas.

Puede que en este momento esté pensando en alternativas muy inteligentes para tratar de resolver los problemas que se plantean en el párrafo anterior, posiblemente mediante la incorporación de nuevas columnas a la clave primaria, pero créame, déjelo, el esfuerzo será inútil, la realidad es que los problemas no desaparecerán, se incrementarán. Por ejemplo, podemos pensar que podremos almacenar menores de edad en nuestra tabla si añadimos una nueva columna a la clave primaria que nos indique si en realidad el DNI es suyo o de su representante legal. Pero ocurre que una persona puede tener varios hijos, con lo cual necesitariamos otra nueva columna para distinguirlos. También podemos pensar que podremos almacenar personas de distintos paises si añadimos una nueva columna que nos indique la nacionalidad de cada persona, permitiendo así además que un mismo número se repita para personas de distintos paises. Pero ocurre que una persona puede tener varias nacionalidades. Y así, vuelta a empezar. Lo normal es que siguiendo esta línea de razonamiento se acabe necesitando facilmente 7 u 8 columnas más.

En lineas generales no se deben utilizar claves primarias compuestas en las tablas maestras, es decir, claves formadas por varias columnas en las tablas que almacenan las entidades básicas que gestiona un sistema. Complica los modelos haciéndolos difíciles de mantener y ampliar. Piense en el ejemplo anterior, en si persitieramos en nuestra idea original de utilizar DNI como clave primaria, de forma que el día de mañana nos encontráramos un problema de los antes citados, y que además lo solucionáramos añadiendo un nuevo campo a la clave primaria. Pues ocurriría que tendriamos que añadir ese nuevo campo a todas las tablas y procesos que hicieran referencia a la tabla de personas. El cambio no sería único y localizado, si no que se expandería como una enfermedad contagiosa por todas las entidades y procesos del sistema que tuvieran algún tipo de relación con la tabla de personas.

Hay que tener cuidado con las claves compuestas, sobre todo porque a veces se presentan camufladas bajo la apariencia de lo que normalmente se denominan “códigos inteligentes”. Un código inteligente es la unión de varias claves en una sola columna, o sea, una clave primaria compuesta con piel de cordero. Un ejemplo típico de este tipo de código son los localizadores, como el de una entrada de cine en la que figura “S07-F12-B08”, lo que vendría a significar “Sala 7” (S07) “Fila 12” (F12) “Butaca 8” (B08). Este tipo de composición se utiliza a veces para identificar las entidades por su ubicación, suponiendo el hecho de que sólo puede haber una entidad en un mismo sitio a un mismo tiempo. El principal problema que plantea este tipo de clave primaria es que a veces, en el mundo real, las entidades se extravían y acaban en localizaciones distintas a las que indican sus códigos, siendo necesario cambiar las claves primarias en la tabla principal y en todas las tablas en las que aparezcan referenciadas, lo que puede ser bastante laborioso, por no decir engorroso y proclive a errores.

El párrafo anterior nos da las dos últimas pistas definitivas para entender el por qué no hay que utilizar DNI como clave primaria de la tabla de personas. La primera pista es que una clave primaria nunca puede depender de información introducida manualmente. Si se hace depender el valor de una clave primaria de lo que escriba un usuario se está creando una dependencia brutal de éste con el funcionamiento interno del sistema. El acoplamiento entre capas de una aplicación siempre debe ser débil, de forma que se pueda modificar cada componente de forma individual sin afectar al resto, y en consecuencia, las reglas que rigen el mundo real no tienen porque influir en las que se establecen en el interior de una aplicación. Si la clave primaria de la tabla persona fuera DNI, y el día de mañana se decidiera utilizar el NIF, entonces se tendría que cambiar todo el modelo y los procesos para gestionar la clave como un campo alfanumérico en vez de como sólo nunérico. Si DNI no fuera clave entonces bastaría con cambiar ese campo concreto de forma aislada.

La segunda pista es que una clave primaria nunca ha de poder modificarse. La clave primaria de un registro identifica de forma unívoca a un objeto dentro del dominio que se está modelando. Si se cambia la clave primaria de un registro se produce una “mutación” por la cual el registro pasa a representar a otro objeto. Este aspecto quizás sea un poco sutil de entender, sobre todo por que conlleva cierto nivel de abstracción. Una persona se podría decir que es la suma de sus características individuales, así como del lugar que ocupa en cualquiera de las dimensiones en las que se proyecta (y posiblemente de su ausencia en las dimensiones en las que no se proyecta). Es una entidad, un ser único y distinguible. Su clave primaria ha de identificarlo de forma unívoca, si se cambia representaría otra entidad, no al individuo original. Piense en términos de variables, como las que se utilizan normalmente en cualquier lenguaje de programación. Primero se definen, y luego se les puede cambiar su contenido, de igual forma que una persona puede cambiarse de ropa o de peinado. Las variables siempren hacen referencia a una misma información concreta, independientemente del valor que contengan en un momento dado. Con las personas ocurre lo mismo, da igual el peinado o la ropa que llevemos, e incluso donde nos encontremos, por encima de todo seguimos siendo nosotros mismos, no otros. Si a una persona se le cambia su DNI porque se introdujo un número equivocado en el alta, debe seguir siendo la misma entidad que se insertó originalmente en el sistema, no otra distinta que implique una actualización masiva de las relaciones.

En definitiva, la forma correcta de identificar las entidades en una tabla es utilizar un código o identificador único que carezca de significado en el mundo real. Un simple valor numérico generado de forma secuencial es suficiente. La idea es crear una tabla con una primera columna de tipo numérico que sirva como clave primaria. En el ejemplo inicial, la tabla de personas, podría ser algo como: ID_PERSON NUMBER(10) NOT NULL. De forma que cuando se inserte una primera persona en la tabla se le asigne el valor 1 como clave primaria (ó 1527, es indistinto). Cuando se inserte una nueva persona se le asigne el 2 (ó 1528). Y así sucesivamente. De esta forma no hay posibilidad de conflictos, o lo que es lo mismo, de encontrarse con errores por claves primarias duplicadas. Cada persona queda emparejada con un identificador que le distingue unívocamente del resto. Y lo dicho para persona sirve para cualquier tipo de entidad. Un motor no se identifica por su número de bastidor, sino por su clave primaria. Un televisor no se identifica por su número de serie, sino por su clave primaria. Una factura no se identifica por su número, sino por su clave primaria. Un usuario no indentifica por su nombre, sino por clave primaria. ¿Captan la idea?

Esta forma de definir las claves suele crear bastante confusión en los desarrolladores no acostumbrados a esta forma de modelar. Las primeras cuestiones que se suelen plantear son la tocantes al rendimiento, sobre todo porque las búsquedas por columnas como DNI son bastantes habituales. Al dejar de ser parte de la clave primaria hay que definir índices adicionales sobre las tablas para estas columnas. Algunos diseñadores reniegan espontáneamente cuando se sugiere añadir varios índices sobre una misma tabla alegando que corresponde a un mal diseño. Pero entonces, ¿para qué diablos queremos los índices?. Otra cuestión habitual que se suele plantear es cómo generar las claves primarias. La respuesta creo que depende en gran medida de las herramientas que se utilicen para desarrollar. Oracle por ejemplo proporciona los objetos de tipo SEQUENCE, que generan valores secuenciales de forma atómica. Algunos gestores permiten indicar que las columnas sean autoincrementales, de forma que cada vez que se inserta un registro se genera automáticamente un nuevo valor. Los “gurús” suelen divagar acerca de aspectos tales como si es necesario utilizar una única secuencia para todas las entidades del sistema o una secuencia distinta para cada tabla. Lo que está claro es que nunca se debe ejecutar COUNT o MAX sobre las tablas para obtener el siguiente secuencial, son atentados directos contra la integridad y el rendimiento de la aplicación. Otra cuestión que se suele plantear es el tema del tamaño requerido por la columna de la clave primaria. Un tamaño de 10 representa una cantidad de diez billones de valores posibles, suficiente para la mayoría de aplicaciones de propósito general. Lo que hay que hacer sobre todo es que todas las tablas tengan una columna de clave primaria con el mismo tamaño, no cada una con un tamaño distinto ajustado en función de su ocupación estimada. Referente a esta última cuestión, hay que matizar que añadir una columna de tamaño diez no implica añadir automáticamente 10 bytes por registro, el espacio adicional requerido dependerá enteramente del gestor de base de datos que se esté utilizando.

Trabajar con tablas definidas de esta forma por primera vez puede resultar un tanto inquietante al principio. Puede parecer que mantener la coherencia de tantos identificadores es algo complicado, pero no lo es, de hecho, hoy en día es la forma más natural de diseñar y trabajar. Simplifica el diseño de las capas de persistencia con las que se consiguen mapear los objetos en memoria, al tiempo que ayuda a separar la información que resulta significativa para los usuarios de la forma en que se gestiona internamente. Si sus herramientas no soportan esta forma de trabajar vaya pensando seriamente en sustituirlas por otras nuevas.

Y por último, no quería dejar de comentar el hecho de que seguramente todos estos razonamientos parezcan complicaciones innecesarias para sistemas pequeños en ambientes muy controlados. Sin embargo, pensar así es olvidar una máxima informática que hay que tener siempre muy presente: ¡los requerimientos siempren cambian!