Claves en bases de datos relacionales
Las bases de datos relacionales organizan la información en tablas. Pero para que esas tablas funcionen correctamente, cada registro necesita una forma clara de identificarse, relacionarse con otros y garantizar que los datos no se repiten o se pierden. La herramienta que permite todo esto son las claves. Comprenderlas es fundamental antes de aprender relaciones, cardinalidad o normalización.
¿Qué es una clave?
Una clave es un atributo o conjunto de atributos que cumplen un propósito concreto dentro de la tabla. Pueden identificar registros, asegurar que los datos no se repitan o conectar una tabla con otra. Aunque más adelante usarás SQL, ahora trabajaremos con DBML, que muestra las claves de forma visual.
Clave primaria (Primary Key - PK)
Es la clave más importante de una tabla. Sirve para identificar cada registro de manera única. No puede repetirse y no puede quedar vacía. Todas las tablas importantes de un proyecto real tienen clave primaria.
Ejemplo en DBML:
Table cliente {
id int [pk, increment] // clave primaria
nombre varchar(100)
correo varchar(150)
}
El atributo id identifica a cada cliente sin ambigüedades. Por qué es necesaria: Si dos clientes se llaman igual, la clave primaria evita confusiones. Además, permite que otras tablas puedan referirse a este cliente de forma segura.
Claves candidatas
En ocasiones, una tabla tiene varios atributos que podrían servir como identificador único. Cada uno de esos atributos es una clave candidata.
Ejemplo de clave candidata en DBML:
Table usuario {
id int [pk, increment]
nombre_usuario varchar(50) [unique] // clave candidata
correo varchar(150) [unique] // clave candidata
}
Aunque id es la clave primaria, nombre_usuario y correo podrían haber cumplido ese papel. La elección depende del diseño: en sistemas reales suele preferirse un identificador numérico para evitar problemas si el usuario cambia su correo o su nombre.
Clave alternativa (Alternative Key - AK)
Es una clave candidata que no ha sido elegida como primaria. En el ejemplo anterior, correo y nombre_usuario serían claves alternativas.
Clave natural y clave sustituta
Estas dos ideas ayudan a entender qué tipo de clave conviene usar.
Clave natural
Es una clave basada en un dato real. Por ejemplo, un número de DNI, un correo o un número de matrícula. El problema es que estos datos pueden cambiar.
Clave sustituta
Es una clave creada artificialmente por el sistema, normalmente un número autoincremental. No tiene significado fuera del sistema. Es estable, simple y no cambia.
En la mayoría de proyectos modernos se utiliza una clave sustituta como primaria y las claves naturales se convierten en únicas o se usan para búsquedas.
Ejemplo DBML:
Table producto {
id int [pk, increment] // clave sustituta
codigo varchar(20) [unique] // clave natural única
nombre varchar(100)
}
Clave ajena (Foreign Key - FK)
Una clave ajena es un atributo que conecta una tabla con otra. Sirve para establecer relaciones: uno a muchos, uno a uno o muchos a muchos.
Se llama ajena porque referencia a una clave primaria de otra tabla.
Ejemplo DBML:
Table categoria {
id int [pk, increment]
nombre varchar(100)
}
Table producto {
id int [pk, increment]
nombre varchar(100)
categoria_id int [ref: > categoria.id] // clave ajena
}
- categoria_id permite relacionar cada producto con una categoría concreta. Si la clave primaria de la categoría cambia o desaparece, el sistema lo detecta y evita inconsistencias.
Claves compuestas
En algunas situaciones no basta con una sola columna para identificar un registro. Una clave compuesta combina dos o más atributos para formar la clave primaria.
Ejemplo:
Table matricula {
alumno_id int
curso_id int
indexes {
(alumno_id, curso_id) [unique, pk]
}
}
Esta tabla no debería permitir que un mismo alumno se matricule dos veces en el mismo curso, y la clave compuesta lo garantiza.
Cuándo usar clave compuesta y cuándo evitarla
Es útil cuando la combinación es realmente la identidad del dato. Sin embargo, en proyectos grandes puede complicar relaciones, por lo que muchas veces se usa una clave sustituta y la combinación se obliga a ser única.
Ejemplo alternativo recomendado:
Table matricula {
id int [pk, increment]
alumno_id int [ref: > alumno.id]
curso_id int [ref: > curso.id]
indexes {
(alumno_id, curso_id) [unique]
}
}
¿Qué ocurre si una tabla no tiene clave?
En la práctica, crea varios problemas:
–Es difícil identificar registros.
– No pueden establecerse relaciones fiables.
– La integridad referencial deja de funcionar.
– La normalización se vuelve poco útil.
Por eso, una tabla sin clave normalmente indica un mal diseño.
Ejercicio práctico sobre claves (con dbml y drawdb)
Vas a diseñar un pequeño modelo de base de datos para una tienda online muy simple. La tienda necesita almacenar:
- Clientes que se registran.
- Productos que se venden.
- Pedidos que hacen los clientes.
- Líneas de cada pedido (qué productos y cuántas unidades se han comprado).
El objetivo del ejercicio es practicar:
- Claves primarias (incluyendo artificiales
id). - Claves candidatas (como email o código de barras).
- Claves foráneas entre tablas relacionadas.
- Una clave primaria compuesta en la tabla de líneas de pedido.
Trabajarás usando dbml y drawdb.
Identificar las tablas y sus claves primarias
Tarea para el alumno:
- Identifica las entidades que se convertirán en tablas.
- Para cada tabla, decide qué columna será su clave primaria.
Guía:
Las entidades mínimas son:
clienteproductopedidolinea_pedido
Solución propuesta en dbml (solo estructura básica con claves primarias):
Table cliente {
id int [pk, increment]
nombre varchar(100)
email varchar(150)
}
Table producto {
id int [pk, increment]
nombre varchar(100)
precio numeric(10,2)
}
Table pedido {
id int [pk, increment]
fecha date
}
Table linea_pedido {
id_pedido int
num_linea int
cantidad int
}
Por ahora linea_pedido no tiene clave primaria declarada, eso lo haremos después con una clave compuesta.
Añadir claves candidatas (unique)
Tarea:
- Decide si algún atributo debería ser único además de la clave primaria.
- Marca estos atributos como
uniqueen dbml.
Pistas:
- Un cliente no debería tener el mismo email que otro cliente.
- Un producto podría tener un código de barras que no se repite.
Solución propuesta:
Table cliente {
id int [pk, increment]
nombre varchar(100)
email varchar(150) [unique] // clave candidata
}
Table producto {
id int [pk, increment]
nombre varchar(100)
codigo_barras char(13) [unique] // clave candidata
precio numeric(10,2)
}
Aquí email y codigo_barras son claves candidatas, pero hemos decidido mantener id como clave primaria por simplicidad.
Añadir claves foráneas para las relaciones
Tarea:
- Piensa qué relación existe entre
clienteypedido. - Piensa qué relación existe entre
pedidoylinea_pedido. - Añade las columnas necesarias y las referencias (
Ref) en dbml.
Guía:
- Cada pedido pertenece a un cliente.
- Cada línea de pedido pertenece a un pedido.
- Más adelante, cada línea de pedido también se relacionará con un producto.
Solución propuesta:
Table cliente {
id int [pk, increment]
nombre varchar(100)
email varchar(150) [unique]
}
Table pedido {
id int [pk, increment]
fecha date
cliente_id int
}
Table linea_pedido {
id_pedido int
num_linea int
producto_id int
cantidad int
}
Table producto {
id int [pk, increment]
nombre varchar(100)
codigo_barras char(13) [unique]
precio numeric(10,2)
}
// Relaciones
Ref: pedido.cliente_id > cliente.id
Ref: linea_pedido.id_pedido > pedido.id
Ref: linea_pedido.producto_id > producto.id
En drawdb, el alumno puede conectar las tablas usando las claves foráneas y ver los enlaces visualmente.
Definir una clave primaria compuesta en linea_pedido
Tarea:
- Decide cuál es la mejor clave primaria para
linea_pedido. - Defínela como clave compuesta en dbml.
Guía:
- Una línea de pedido depende de dos cosas:
- De qué pedido es (
id_pedido). - Qué número de línea tiene dentro de ese pedido (
num_linea).
- De qué pedido es (
- La combinación
(id_pedido, num_linea)identifica de forma única cada línea.
Solución propuesta:
Table linea_pedido {
id_pedido int
num_linea int
producto_id int
cantidad int
indexes {
(id_pedido, num_linea) [pk] // clave primaria compuesta
}
}
En drawdb, el alumno verá ambas columnas marcadas como parte de la clave primaria. Esto refleja que la clave de linea_pedido es compuesta.
Modelo completo del ejercicio (solución final)
Este es el modelo final en dbml, para que el alumno compare su solución con la propuesta:
Table cliente {
id int [pk, increment]
nombre varchar(100)
email varchar(150) [unique]
}
Table producto {
id int [pk, increment]
nombre varchar(100)
codigo_barras char(13) [unique]
precio numeric(10,2)
}
Table pedido {
id int [pk, increment]
fecha date
cliente_id int
}
Table linea_pedido {
id_pedido int
num_linea int
producto_id int
cantidad int
indexes {
(id_pedido, num_linea) [pk]
}
}
Ref: pedido.cliente_id > cliente.id
Ref: linea_pedido.id_pedido > pedido.id
Ref: linea_pedido.producto_id > producto.id
Con este ejercicio el alumno ha practicado:
- Claves primarias simples (
iden varias tablas). - Claves candidatas (
email,codigo_barras). - Claves foráneas (
cliente_id,id_pedido,producto_id). - Una clave primaria compuesta (
id_pedido,num_linea).