3.3 ALTER, DROP e integridad referencial¶
ALTER TABLE: cambiar la estructura¶
ALTER TABLE modifica una tabla que ya existe y tiene datos.
Añadir una columna:
ALTER TABLE alumnos ADD COLUMN email TEXT;
UPDATE alumnos SET email = LOWER(nombre) || '@instituto.es' WHERE id <= 3;
SELECT id, nombre, email FROM alumnos WHERE id <= 4;
Resultado:
| id | nombre | |
|---|---|---|
| 1 | Ana | ana@instituto.es |
| 2 | Luis | luis@instituto.es |
| 3 | Marta | marta@instituto.es |
| 4 | Pablo | NULL |
4 filas
Las filas que ya existían reciben NULL en la columna nueva (o su DEFAULT). Por eso una columna nueva NOT NULL necesita un valor por defecto.
Renombrar una columna o una tabla:
ALTER TABLE profesores RENAME COLUMN nombre TO nombre_completo;
SELECT * FROM profesores;
Resultado:
| id | nombre_completo |
|---|---|
| 1 | Marta Ruiz |
| 2 | Pedro Salas |
| 3 | Lucía Ferrer |
| 4 | Hugo Navas |
4 filas
Borrar una columna:
ALTER TABLE alumnos DROP COLUMN ciudad;
SELECT * FROM alumnos LIMIT 3;
Resultado:
| id | nombre | apellido | nacimiento | id_grupo |
|---|---|---|---|---|
| 1 | Ana | Gil | 2005-03-14 | 1 |
| 2 | Luis | Romero | 2004-11-02 | 1 |
| 3 | Marta | Díaz | 2005-07-21 | 1 |
3 filas
Borrar una columna elimina sus datos para siempre.
| Operación | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| Añadir columna | ADD COLUMN |
ADD COLUMN |
ADD COLUMN |
| Cambiar el tipo | No se puede (hay que recrear la tabla) | MODIFY COLUMN |
ALTER COLUMN ... TYPE |
| Añadir una restricción después | No se puede (hay que recrear la tabla) | ADD CONSTRAINT |
ADD CONSTRAINT |
SQLite y los cambios de estructura
SQLite permite pocos cambios con ALTER TABLE. Para el resto se crea una tabla nueva, se copian los datos con INSERT ... SELECT, se borra la antigua y se renombra la nueva. Es un truco habitual cuando se trabaja con SQLite.
DROP TABLE: borrar una tabla¶
DROP TABLE elimina la tabla con todos sus datos, sin pedir confirmación:
CREATE TABLE temporal (id INTEGER PRIMARY KEY);
DROP TABLE temporal;
SELECT name AS tabla FROM sqlite_master WHERE type = 'table' ORDER BY name;
Resultado:
| tabla |
|---|
| alumnos |
| asignaturas |
| grupos |
| notas |
| profesores |
5 filas
DROP TABLE IF EXISTS nombre; no da error si la tabla no existe. Si otras tablas dependen de ella por una clave foránea, el motor lo impide:
DROP TABLE grupos;
Resultado: error
IntegrityError: FOREIGN KEY constraint failed
Integridad referencial: qué pasa al borrar o cambiar un «padre»¶
Al declarar una clave foránea se puede decidir qué ocurre con las filas hijas cuando se borra (ON DELETE) o se cambia (ON UPDATE) la fila padre:
| Opción | Qué hace |
|---|---|
RESTRICT / NO ACTION |
Impide borrar el padre mientras tenga hijos (es lo habitual por defecto) |
CASCADE |
Borra también las filas hijas |
SET NULL |
Pone NULL en la clave foránea de los hijos |
SET DEFAULT |
Pone el valor por defecto |
Se ve claramente con un ejemplo: dos tipos de hijos, uno con CASCADE (los comentarios de una entrada no tienen sentido sin ella) y otro con SET NULL (los autores sobreviven):
CREATE TABLE entradas (id INTEGER PRIMARY KEY, titulo TEXT NOT NULL);
CREATE TABLE comentarios (
id INTEGER PRIMARY KEY,
id_entrada INTEGER NOT NULL REFERENCES entradas(id) ON DELETE CASCADE,
texto TEXT NOT NULL
);
CREATE TABLE descargas (
id INTEGER PRIMARY KEY,
id_entrada INTEGER REFERENCES entradas(id) ON DELETE SET NULL,
usuario TEXT NOT NULL
);
INSERT INTO entradas VALUES (1, 'Hola mundo');
INSERT INTO comentarios VALUES (1, 1, 'Bien'), (2, 1, 'Genial');
INSERT INTO descargas VALUES (1, 1, 'ana');
DELETE FROM entradas WHERE id = 1;
SELECT (SELECT COUNT(*) FROM comentarios) AS comentarios, (SELECT COUNT(*) FROM descargas) AS descargas, (SELECT id_entrada FROM descargas) AS entrada_de_la_descarga;
Resultado:
| comentarios | descargas | entrada_de_la_descarga |
|---|---|---|
| 0 | 1 | NULL |
1 fila
Al borrar la entrada, se han borrado sus 2 comentarios (CASCADE) y la descarga ha quedado con la entrada a NULL (SET NULL).
SQLite necesita activar las claves foráneas
En SQLite las claves foráneas no se comprueban hasta ejecutar PRAGMA foreign_keys = ON; en cada conexión. Todos los ejemplos de esta web lo tienen activado. MySQL (con InnoDB), PostgreSQL y Oracle las comprueban siempre.
CASCADE con cuidado
Un borrado en cascada puede eliminar muchísimos datos de golpe sin avisar. Úsalo solo cuando los hijos no tienen sentido sin el padre.
CREATE TABLE ... AS SELECT: copiar el resultado de una consulta¶
Crea una tabla nueva con el resultado de un SELECT: útil para copias, informes o pruebas:
CREATE TABLE aprobados_1 AS
SELECT id_alumno, id_asignatura, nota FROM notas WHERE convocatoria = 1 AND nota >= 5;
SELECT COUNT(*) AS filas FROM aprobados_1;
Resultado:
| filas |
|---|
| 35 |
1 fila
La tabla copia las columnas y los datos, pero no las restricciones (ni claves ni NOT NULL).
Errores frecuentes¶
| Error | Cómo evitarlo |
|---|---|
DROP TABLE en el sitio equivocado |
Comprueba la base de datos a la que estás conectado |
Añadir una columna NOT NULL sin DEFAULT a una tabla con datos |
Dar un valor por defecto |
Usar CASCADE «por si acaso» |
Decidir fila por fila qué debe pasar con los hijos |
Esperar que ALTER TABLE lo haga todo en SQLite |
Recrear la tabla para los cambios grandes |
| Cambiar la estructura sin copia de seguridad | Copia antes de ALTER y DROP |
Para practicar¶
Los ejercicios S3.8 a S3.12 de S3 · Ejercicios combinan estas sentencias.