Entendiendo las relaciones de Bases de Datos

Nadie dijo que relacionar dos tablas de tu base de datos fuera sencillo...

Entendiendo las relaciones de Bases de Datos
Photo by Richard Payette / Unsplash

Cuando empiezas a programar y entras en el maravilloso mundo de las bases de datos, una de las cosas que más te confunde son las relaciones entre tablas. He tenido largas conversaciones acerca de los distintos tipos de relaciones de bases de datos, y a veces han resultado más liosas de lo que parece. Las relaciones en la base de datos tienen muchos matices, como por ejemplo que la columna se convierte en un índice, si podemos agregar constraints a las mismas...

El tema de las constraints en el esquema de datos ya da para un post por sí solo, por lo que en este post intentaré esclarecer las diferencias entre los distintos tipos de relaciones.


Los distintos tipos de relaciones

Existen un total de tres tipos de relaciones.

  • Relaciones uno a uno (1:1)
  • Relaciones uno a muchos (1:n)
  • Relaciones muchos a muchos (n:n)

He llegado a ver que se explica que hay relaciones (0:x), entiendo que se refieren a relaciones las cuales pueden ser NULLABLE. Hay que entender que puedes hacer los datos NULLABLE si lo necesitas, por lo que me limitaré a indicar las relaciones de la manera anteriormente indicada.


Relaciones uno a uno (1:1)

Es el tipo de relación más sencillo. En cada tabla se crea una columna con un identificador a la otra.

Pongamos el caso de que queremos tener un registro en la BBDD de nuestra empresa en el cual guardamos a nuestros desarrolladores y el proyecto en el que trabajan actualmente.

En nuestro ejemplo, un desarrollador solo puede tener un proyecto asignado y viceversa. Queremos usar estas relaciones para obligar a que los registros vayan en parejas.

Una relación 1:1 es muy sencilla de utilizar, puesto que también podemos hacer queries en las que accedemos a la información de la tabla relacionada en ambos sentidos mediante un JOIN.


Relaciones uno a muchos (1:n)

Aquí la clave foránea solo existe en una tabla, la tabla en la que muchos registros se vinculan a uno solo.

Aquí pasamos al caso de una BBDD en la que almacenamos nuestros clientes y sus propiedades (casas, terrenos... bienes inmuebles en general). Como podemos ver, la única tabla que guarda una clave foránea es la de las propiedades. Esto es debido a que una propiedad debe pertenecer a un cliente, pero un cliente puede poseer muchas propiedades.

Antes he comentado que en las relaciones 1:1 puedes usar un JOIN para juntar los datos de ambas tablas en ambos sentidos. Aquí no es el caso.

¿Esto es un problema a la hora de crear mis aplicaciones? Claro que no. Hoy en día los ORM implementan el concepto de relaciones "inversas". Esto te permite referenciar a los registros que se relacionan desde la otra tabla sin tener que hacer queries complejas buscándolos. Ten en cuenta que estas relaciones son virtuales, por lo que no existen en el propio sistema de base de datos.

💡
Dejo enlaces a las documentaciones de algunos ORMs y frameworks que introducen este concepto.
- Hibernate
- Django (en inglés, no parece estar traducido)
- Sequelize (en inglés, explica el por qué las relaciones se definen en pares)

Esto es algo que se puede usar en las relaciones uno a muchos y muchos a muchos, donde se complica aún más todo esto.


Relaciones muchos a muchos (n:n)

Este es el tipo de relación más complejo. Esto se debe a que hay una tabla intermedia. Para entender esto mejor, veamos el último ejemplo.

Tenemos en nuestro ejemplo que necesitamos mantener registros de nuestros clientes, de los dulces que tenemos en inventario y los pedidos de nuestros clientes. Un cliente puede tener muchos pedidos, y un dulce puede pertenecer a muchos pedidos, por lo que no nos queda otra que crear una tabla intermedia, la tabla de pedidos.

La tabla de pedidos es la tabla intermedia de nuestra relación muchos a muchos (n:n), y es esta la única que posee las claves foráneas de la relación. Aprovechando que nuestra relación necesita de una tabla nueva, ¿por qué no le agregamos algo de información extra? Podemos agregar la cantidad de dulces que quiere nuestro cliente en cada pedido.

Cabe destacar que la tabla intermedia no necesariamente tiene por qué llevar una clave primaria o un identificador. En este caso se agrega porque en la aplicación querremos identificar los pedidos por separado de una manera rápida (por ejemplo, para páginas de detalles del pedido). Si es el caso de que la tabla tan solo se compone de claves foráneas se habla de una tabla de claves candidatas.

Si confunde esto, piénsalo de la siguiente manera: tenemos dos relaciones uno a muchos (1:n):

  • Una relación en la que un cliente puede tener muchos pedidos.
  • Una relación en la que cada dulce puede ser solicitado en muchos pedidos.

De esta manera, en el esquema general de las cosas, vemos que los clientes y los dulces están relacionados de muchos a muchos (n:n).


Una recomendación para concluir

Una cosa que recomiendo mucho a la hora de diseñar la base de datos de una aplicación es crear un esquema visual de entidad-relación.

El programa con el que he creado los ejemplos anteriores (y me gusta utilizar para crear cualquier tipo de esquema) es draw.io. Te permite guardar los esquemas a tu ordenador o a tu almacenamiento en la nube preferido.