Claves foráneas: vínculos entre tablas
En resumen: REFERENCES convierte una columna en clave foránea, y ON DELETE (CASCADE, SET NULL, RESTRICT) dice qué pasa con las filas que apuntan a una fila borrada.
Problema
Borra mi cuenta
Maria Ivanova escribe: «Por favor, borra mi cuenta.» Lo intentas:
La base de datos se niega. Maria tiene 12 pedidos, y cada uno apunta a ella. Si la clienta desaparece, los pedidos apuntarán a nadie.
¿Qué debe pasar con sus pedidos? ¿Y con sus reseñas? ¿Y con su lista de productos favoritos? La respuesta es distinta para cada tabla. Y está escrita en el esquema.
La idea
REFERENCES
Ya conoces la clave foránea: una columna que apunta a una fila de otra tabla. En CREATE TABLE se escribe con REFERENCES tabla(columna). Así están hechos los pedidos de la tienda:
orders
- PK
idinteger - FK
customer_idinteger *→ customers ordered_attext *statustext *
Aquí hay una tabla nueva: los productos favoritos de cada cliente. La primera fila entra, la segunda apunta al cliente 99, que no existe:
Ahora la base de datos vigila los vínculos ella misma. Una fila que apunta a un cliente o a un producto que no existe no entra.
Pruébalo
Los productos favoritos
La tabla wishlist ya tiene sus columnas. Haz que customer_id apunte a customers y product_id a products.
Solución
CREATE TABLE wishlist (
customer_id INTEGER NOT NULL REFERENCES customers(id),
product_id INTEGER NOT NULL REFERENCES products(id),
PRIMARY KEY (customer_id, product_id)
);customer_id INTEGER NOT NULL REFERENCES customers(id) y product_id INTEGER NOT NULL REFERENCES products(id). Si no escribes ninguna columna después del nombre de la tabla, SQLite supone que apuntas a su clave primaria.
La idea
ON DELETE CASCADE
Después de REFERENCES puedes decir qué debe pasar cuando se borra la fila a la que apuntas. Eso es ON DELETE.
CASCADE significa «bórrame a mí también». Yavor Penev (cliente 20) tiene dos productos favoritos. Teodora Genova (cliente 19) tiene uno. Yavor borra su cuenta:
Los favoritos de Yavor se fueron con él. Los de Teodora siguen ahí. Una lista de favoritos no tiene sentido sin el cliente, así que CASCADE es lo correcto aquí.
La idea
SET NULL y RESTRICT
SET NULL significa «consérvame, pero sin el vínculo». Las preguntas sobre productos también son útiles para otros compradores, aunque el autor se haya ido:
La pregunta se quedó, y customer_id pasó a NULL: una pregunta de un cliente que ya no está.
RESTRICT significa «no permitas el borrado». Las facturas hay que conservarlas durante años por ley:
Sin ON DELETE el comportamiento es casi el mismo: el borrado se detiene. Por eso Maria no pudo borrar su cuenta. Los pedidos son la historia de la tienda y no deben desaparecer.
Más precisamente, para curiosos
Sin ON DELETE la regla es NO ACTION. La única diferencia con RESTRICT es cuándo se comprueba: RESTRICT se detiene al instante, mientras que NO ACTION comprueba al final de la sentencia (o de la transacción, si la comprobación está aplazada). También existe ON UPDATE para cambiar la clave, pero se usa poco, porque una clave primaria casi nunca cambia. ON DELETE SET DEFAULT también es raro.
Míralo
SET NULL contra NOT NULL
Alguien escribió la tabla de preguntas así:
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE SET NULL
Yavor borra su cuenta otra vez:
SET NULL quiere guardar NULL, y NOT NULL no lo permite. El borrado se detiene. SQLite no te avisa de esto en CREATE TABLE, solo cuando alguien intenta borrar. Una columna con SET NULL debe poder quedar vacía.
Pruébalo
Las direcciones de entrega
La tabla addresses guarda las direcciones de los clientes. Cuando un cliente borra su cuenta, sus direcciones deben desaparecer con él. Corrige customer_id.
Solución
CREATE TABLE addresses (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
city TEXT NOT NULL,
street TEXT NOT NULL
);REFERENCES customers(id) ON DELETE CASCADE. Una dirección sin cliente no le sirve a nadie, y son datos personales que no deben quedarse.
La idea
La comprobación tiene que estar activada
En el curso la base de datos comprueba las claves foráneas. Se lo preguntas con PRAGMA, el comando de SQLite para sus ajustes:
1 significa «activada». Si la desactivas, REFERENCES ya no vigila nada:
Un pedido del cliente 99, que no existe, entró sin ningún error.
Más precisamente, para curiosos
En SQLite real la comprobación está desactivada por defecto. Abre sqlite3 en tu computadora y escribe PRAGMA foreign_keys;: la respuesta es 0. La razón son los programas antiguos escritos antes de que SQLite comprobara las claves foráneas. El ajuste vale solo para una conexión a la base de datos. Por eso un programa ejecuta PRAGMA foreign_keys = ON; cada vez que abre la base. Esta página lo hace por ti. PostgreSQL, MySQL (con InnoDB) y SQL Server comprueban las claves foráneas por defecto.
Ejercicio
Preguntas sobre productos
La tabla questions tiene id, product_id, customer_id y body. Cuando un producto se retira de la tienda, las preguntas sobre él desaparecen. Cuando un cliente borra su cuenta, sus preguntas se quedan, pero sin autor. Añade los vínculos.
Solución
CREATE TABLE questions (
id INTEGER PRIMARY KEY,
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
body TEXT NOT NULL
);product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE y customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL. customer_id no tiene NOT NULL, porque si no SET NULL no podría funcionar.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: INSERT: insertar filas · DELETE: borrar filas · CREATE TABLE: crear una tabla · Restricciones: reglas para los datos
- Continúa en: ALTER TABLE: cambiar una tabla · Tipos de relación: 1:1, 1:N, M:N · Diagrama ER: de la descripción al esquema · Normalización
- Ejercicios sobre el tema: Tablas y reglas