2.2 Del modelo a las tablas¶
Con el modelo entidad-relación hecho, pasar a tablas es mecánico: se siguen unas reglas.
Las reglas de transformación¶
| Elemento del modelo | Se convierte en... |
|---|---|
| Entidad | Una tabla, con una clave primaria |
| Atributo simple | Una columna |
| Atributo compuesto | Una columna por cada parte |
| Atributo multivaluado | Una tabla aparte, con una clave foránea hacia la entidad |
| Atributo derivado | Nada: no se guarda |
| Relación 1:N | Una clave foránea en la tabla del lado «N» (el lado «muchos») |
| Relación N:M | Una tabla intermedia con dos claves foráneas (y los atributos de la relación); su clave primaria es la pareja de ambas |
| Relación 1:1 | Una clave foránea con UNIQUE (o, si siempre van juntas, fundir las dos entidades en una tabla) |
La regla del 1:N es la que más se confunde: la clave foránea va donde está el «muchos». Un grupo tiene muchos alumnos, así que la columna id_grupo está en alumnos, no al revés. (Si se pusiera en grupos, cada grupo solo podría tener un alumno.)
Un ejemplo completo: la tienda¶
El modelo del apartado anterior (categorías, productos, clientes, pedidos y la relación N:M entre pedidos y productos) se convierte en cinco tablas:
CREATE TABLE categorias (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL UNIQUE
);
CREATE TABLE productos (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
precio REAL NOT NULL,
id_categoria INTEGER NOT NULL REFERENCES categorias(id)
);
CREATE TABLE clientes (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE pedidos (
id INTEGER PRIMARY KEY,
fecha TEXT NOT NULL,
id_cliente INTEGER NOT NULL REFERENCES clientes(id)
);
CREATE TABLE lineas_pedido (
id_pedido INTEGER NOT NULL REFERENCES pedidos(id),
id_producto INTEGER NOT NULL REFERENCES productos(id),
cantidad INTEGER NOT NULL,
PRIMARY KEY (id_pedido, id_producto)
);
SELECT name AS tabla FROM sqlite_master WHERE type = 'table' ORDER BY name;
Resultado:
| tabla |
|---|
| categorias |
| clientes |
| lineas_pedido |
| pedidos |
| productos |
5 filas
Mira cómo se ha aplicado cada regla:
- Cada entidad tiene su tabla con su
idcomo clave primaria. productos.id_categoriaes la clave foránea de la relación 1:N entre categorías y productos (el «muchos» son los productos).pedidos.id_clientees la de la relación 1:N entre clientes y pedidos.lineas_pedidoes la tabla intermedia de la relación N:M entre pedidos y productos. Tiene dos claves foráneas y el atributo propio de la relación (cantidad). Su clave primaria es la pareja(id_pedido, id_producto): así, un mismo producto no puede aparecer dos veces en el mismo pedido (se sumaría la cantidad).
Qué impiden las claves foráneas¶
Las claves foráneas protegen la coherencia: no se puede apuntar a algo que no existe. Aquí se intenta registrar una línea de un producto inexistente:
CREATE TABLE categorias (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL UNIQUE
);
CREATE TABLE productos (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
precio REAL NOT NULL,
id_categoria INTEGER NOT NULL REFERENCES categorias(id)
);
CREATE TABLE clientes (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE pedidos (
id INTEGER PRIMARY KEY,
fecha TEXT NOT NULL,
id_cliente INTEGER NOT NULL REFERENCES clientes(id)
);
CREATE TABLE lineas_pedido (
id_pedido INTEGER NOT NULL REFERENCES pedidos(id),
id_producto INTEGER NOT NULL REFERENCES productos(id),
cantidad INTEGER NOT NULL,
PRIMARY KEY (id_pedido, id_producto)
);
INSERT INTO lineas_pedido VALUES (1, 99, 2);
Resultado: error
IntegrityError: FOREIGN KEY constraint failed
(El mensaje exacto cambia según el motor, pero todos rechazan la operación.)
Claves: naturales y artificiales¶
La clave primaria puede ser un dato natural del mundo real (el DNI, el ISBN) o un número artificial (id) que el sistema genera.
| Natural | Artificial (id autonumérico) |
|
|---|---|---|
| Ventaja | Tiene significado | Nunca cambia y es corta |
| Inconveniente | Puede cambiar o repetirse, o ser larga | No significa nada para el usuario |
Lo habitual es usar un id artificial como clave primaria y marcar el dato natural (el correo, el DNI) como UNIQUE, como se ha hecho con clientes.email.
Un 1:1 y un multivaluado¶
Relación 1:1. Un cliente puede tener, como mucho, un perfil con sus preferencias. La clave foránea lleva UNIQUE, para que dos perfiles no apunten al mismo cliente:
CREATE TABLE clientes (id INTEGER PRIMARY KEY, nombre TEXT NOT NULL);
CREATE TABLE perfiles (
id INTEGER PRIMARY KEY,
id_cliente INTEGER NOT NULL UNIQUE REFERENCES clientes(id),
idioma TEXT NOT NULL DEFAULT 'es'
);
SELECT name AS tabla FROM sqlite_master WHERE type = 'table' ORDER BY name;
Resultado:
| tabla |
|---|
| clientes |
| perfiles |
2 filas
Atributo multivaluado. Un cliente puede tener varios teléfonos. En lugar de una columna telefonos con una lista (¡nunca!), se crea una tabla aparte con una clave foránea:
CREATE TABLE clientes (id INTEGER PRIMARY KEY, nombre TEXT NOT NULL);
CREATE TABLE telefonos (
id INTEGER PRIMARY KEY,
id_cliente INTEGER NOT NULL REFERENCES clientes(id),
telefono TEXT NOT NULL
);
INSERT INTO clientes VALUES (1, 'Ana');
INSERT INTO telefonos (id_cliente, telefono) VALUES (1, '600111222'), (1, '956333444');
SELECT c.nombre, t.telefono FROM clientes c JOIN telefonos t ON t.id_cliente = c.id;
Resultado:
| nombre | telefono |
|---|---|
| Ana | 600111222 |
| Ana | 956333444 |
2 filas
(Los JOIN se explican en la unidad 4; aquí solo sirven para ver los dos teléfonos de Ana.)
Errores frecuentes¶
| Error | Cómo evitarlo |
|---|---|
| Poner la clave foránea en el lado «1» de un 1:N | Va donde está el «muchos» |
Resolver un N:M con una columna que guarda varios valores ("3,5,8") |
Una tabla intermedia |
| Una tabla intermedia sin clave primaria compuesta | La pareja de claves foráneas es la clave primaria |
Olvidar UNIQUE en una relación 1:1 |
Sin él, es en realidad un 1:N |
| Guardar un dato que ya está en otra tabla | Guarda solo la clave y consulta el dato (ver la normalización) |
Para practicar¶
Los ejercicios S2.1 a S2.4 de S2 · Ejercicios piden escribir las tablas de un enunciado.