Saltar al contenido
alter-y-drop.sql · devschool

ALTER TABLE y DROP en SQL

Lección 17 de 22 · 12 min de lectura · Actualizado el

En esta lección
  1. La tabla de ejemplo
  2. Añadir columnas: ADD COLUMN
  3. Borrar columnas: DROP COLUMN
  4. Renombrar columnas: RENAME COLUMN
  5. Cambiar el tipo de una columna
  6. Añadir y quitar restricciones
  7. Renombrar tablas
  8. DROP TABLE
  9. DROP DATABASE
  10. TRUNCATE: vaciar una tabla
  11. Precauciones
  12. Migraciones en proyectos reales
  13. Errores frecuentes
  14. Resumen

Ninguna base de datos se queda como se diseñó el primer día. La tienda empieza a vender en otro país y necesita una columna pais, un campo se queda corto, una regla nueva obliga a añadir una restricción. ALTER TABLE cambia la estructura de una tabla que ya existe, sin perder sus datos. DROP y TRUNCATE borran: tablas enteras, bases de datos o todo el contenido de una tabla. Son instrucciones potentes y sin vuelta atrás, así que al final verás cómo trabajar con ellas de forma segura.

La tabla de ejemplo

Partimos de una tabla de libros sencilla, con dos filas:

CREATE TABLE libros (
  id INT AUTO_INCREMENT PRIMARY KEY,
  titulo VARCHAR(100) NOT NULL,
  autor VARCHAR(100),
  precio DECIMAL(6, 2)
);

INSERT INTO libros (titulo, autor, precio) VALUES
  ('Nada', 'Carmen Laforet', 18.50),
  ('La sombra del viento', 'Carlos Ruiz Zafón', 19.95);

(En SQLite, cambia la primera columna por id INTEGER PRIMARY KEY; lo viste en CREATE TABLE.)

Añadir columnas: ADD COLUMN

ALTER TABLE libros ADD COLUMN editorial VARCHAR(80);
SELECT * FROM libros;
idtituloautorprecioeditorial
1NadaCarmen Laforet18.50NULL
2La sombra del vientoCarlos Ruiz Zafón19.95NULL

La columna nueva aparece al final y, en las filas que ya existían, vale NULL. Si le das un valor por defecto, las filas existentes lo reciben:

ALTER TABLE libros ADD COLUMN stock INT NOT NULL DEFAULT 1;
SELECT * FROM libros;
idtituloautorprecioeditorialstock
1NadaCarmen Laforet18.50NULL1
2La sombra del vientoCarlos Ruiz Zafón19.95NULL1

Una columna NOT NULL sin valor por defecto

¿Qué pasa si añades una columna obligatoria sin DEFAULT a una tabla que ya tiene filas?

ALTER TABLE libros ADD COLUMN isbn CHAR(13) NOT NULL;

Cada gestor reacciona distinto:

  • PostgreSQL da error: column “isbn” contains null values.
  • SQLite da error: Cannot add a NOT NULL column with default value NULL.
  • MySQL la añade y rellena las filas existentes con un valor “vacío” ('' en textos, 0 en números). No falla, pero te deja datos sin sentido.

La forma correcta es en tres pasos: añadir la columna permitiendo NULL, rellenarla con un UPDATE y, después, hacerla obligatoria.

Nota: en MySQL puedes elegir la posición de la columna nueva con AFTER otra_columna o FIRST: ADD COLUMN editorial VARCHAR(80) AFTER autor. En PostgreSQL y SQLite siempre va al final.

Borrar columnas: DROP COLUMN

ALTER TABLE libros DROP COLUMN editorial;

La columna desaparece con todos sus datos. No hay papelera ni confirmación.

Nota: SQLite admite DROP COLUMN desde la versión 3.35 (2021), y no deja borrar columnas que formen parte de la clave primaria, de un índice UNIQUE o de una clave foránea.

Renombrar columnas: RENAME COLUMN

ALTER TABLE libros RENAME COLUMN precio TO precio_venta;
SELECT * FROM libros;
idtituloautorprecio_ventastock
1NadaCarmen Laforet18.501
2La sombra del vientoCarlos Ruiz Zafón19.951

Esta sintaxis funciona en MySQL 8, PostgreSQL y SQLite (desde 3.25). En MySQL 5.7 y en MariaDB antiguo se usa CHANGE, que obliga a repetir el tipo: CHANGE precio precio_venta DECIMAL(6, 2).

Cuidado: renombrar una columna rompe todas las consultas de tu aplicación que usen el nombre antiguo. Busca en el código antes de hacerlo.

Cambiar el tipo de una columna

Aquí es donde más cambia la sintaxis entre gestores. Queremos que titulo admita hasta 200 caracteres:

-- MySQL / MariaDB: MODIFY con la definición COMPLETA
ALTER TABLE libros MODIFY COLUMN titulo VARCHAR(200) NOT NULL;

-- PostgreSQL: ALTER COLUMN ... TYPE
ALTER TABLE libros ALTER COLUMN titulo TYPE VARCHAR(200);

En MySQL, MODIFY sustituye la definición entera de la columna. Si escribes solo MODIFY COLUMN titulo VARCHAR(200), pierdes el NOT NULL (y el DEFAULT, si tenía). Es un error muy habitual: repite siempre todas las opciones.

PostgreSQL separa cada cambio en una orden distinta:

ALTER TABLE libros ALTER COLUMN autor SET NOT NULL;
ALTER TABLE libros ALTER COLUMN autor DROP NOT NULL;
ALTER TABLE libros ALTER COLUMN stock SET DEFAULT 0;
ALTER TABLE libros ALTER COLUMN stock DROP DEFAULT;

SET DEFAULT y DROP DEFAULT también funcionan así en MySQL.

Si reduces un tipo y algún dato no cabe, la base de datos se niega. Con los datos de ejemplo, autor a VARCHAR(5) falla en PostgreSQL con value too long for type character varying(5) (MySQL en modo estricto también da error). Ampliar siempre es seguro; reducir, no.

SQLite: ALTER TABLE limitado

SQLite solo permite renombrar la tabla, renombrar columnas, añadir columnas y borrar columnas. No puede cambiar el tipo de una columna ni añadir o quitar restricciones. Para eso hay que reconstruir la tabla:

-- 1. Crear la tabla nueva con la estructura deseada
CREATE TABLE libros_nueva (
  id INTEGER PRIMARY KEY,
  titulo VARCHAR(200) NOT NULL,
  autor VARCHAR(100) NOT NULL,
  precio DECIMAL(6, 2),
  stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0)
);

-- 2. Copiar los datos
INSERT INTO libros_nueva (id, titulo, autor, precio, stock)
SELECT id, titulo, autor, precio, stock FROM libros;

-- 3. Borrar la antigua y renombrar la nueva
DROP TABLE libros;
ALTER TABLE libros_nueva RENAME TO libros;

Hazlo dentro de una transacción para que, si algo falla a mitad, no se quede a medias.

Añadir y quitar restricciones

Las restricciones también se pueden añadir después. Por eso es tan útil ponerles nombre con CONSTRAINT. (La tabla prestamos es la de la biblioteca de CREATE TABLE.)

ALTER TABLE libros ADD CONSTRAINT chk_libros_stock CHECK (stock >= 0);
ALTER TABLE libros ADD CONSTRAINT uq_libros_titulo UNIQUE (titulo);
ALTER TABLE prestamos ADD CONSTRAINT fk_prestamos_libro
  FOREIGN KEY (libro_id) REFERENCES libros(id);

Al añadir una restricción, la base de datos comprueba las filas que ya existen. Si alguna la incumple (dos títulos repetidos, un stock negativo), da error y no la crea. Primero limpia los datos, luego añade la regla.

Para quitarlas:

-- PostgreSQL y MySQL 8.0.19 o posterior
ALTER TABLE libros DROP CONSTRAINT chk_libros_stock;

-- MySQL, forma clásica (según el tipo de restricción)
ALTER TABLE libros DROP CHECK chk_libros_stock;
ALTER TABLE libros DROP INDEX uq_libros_titulo;          -- UNIQUE
ALTER TABLE prestamos DROP FOREIGN KEY fk_prestamos_libro;
ALTER TABLE libros DROP PRIMARY KEY;

En MySQL, una restricción UNIQUE es en realidad un índice, y por eso se borra con DROP INDEX.

Renombrar tablas

-- MySQL
RENAME TABLE libros TO catalogo;

-- MySQL, PostgreSQL y SQLite
ALTER TABLE libros RENAME TO catalogo;

Las claves foráneas que apuntaban a libros se actualizan solas y siguen funcionando. Las consultas de tu aplicación, en cambio, no.

DROP TABLE

DROP TABLE borra la tabla entera: estructura, datos, índices y restricciones.

DROP TABLE catalogo;
DROP TABLE IF EXISTS catalogo;   -- sin error si no existe

Sin IF EXISTS, borrar una tabla que no existe da error (no such table, Unknown table). Con IF EXISTS es ideal para scripts que se ejecutan varias veces.

Si otra tabla tiene una clave foránea que apunta a la que quieres borrar, la base de datos lo impide. Con las tablas del curso:

DROP TABLE clientes;
-- Error: pedidos.cliente_id apunta a clientes
-- PostgreSQL: cannot drop table clientes because other objects depend on it

Tienes que borrar antes la tabla hija (pedidos) o su clave foránea. PostgreSQL ofrece DROP TABLE clientes CASCADE, que elimina la restricción de pedidos (no la tabla ni sus datos).

DROP DATABASE

DROP DATABASE IF EXISTS biblioteca;

Borra la base de datos completa con todas sus tablas. En un servidor de producción, esta línea puede acabar con años de datos en un segundo. En SQLite no existe: se borra el archivo.

TRUNCATE: vaciar una tabla

TRUNCATE borra todas las filas pero deja la tabla (estructura, restricciones e índices):

TRUNCATE TABLE catalogo;

¿En qué se diferencia de DELETE FROM catalogo; sin WHERE?

DELETE FROM tablaTRUNCATE TABLE tabla
Qué borraLas filas que cumplan el WHERE (o todas)Siempre todas
VelocidadFila a fila: lento con millonesCasi instantáneo
Contador de idsSigue donde estabaVuelve a empezar (MySQL; en PostgreSQL con RESTART IDENTITY)
Deshacer con ROLLBACKSíNo en MySQL; sí en PostgreSQL
Con claves foráneas que apuntan a ellaComprueba fila a filaDa error

SQLite no tiene TRUNCATE: usa DELETE FROM tabla;, que internamente ya está optimizado para vaciarla rápido.

Precauciones

ALTER, DROP y TRUNCATE pertenecen al DDL (Data Definition Language), las instrucciones que cambian la estructura. En MySQL tienen una trampa: cada una hace un COMMIT automático, así que no se pueden deshacer aunque estés dentro de una transacción. PostgreSQL sí permite deshacer la mayoría con ROLLBACK.

Por eso, antes de tocar la estructura de datos que importan:

  1. Haz una copia de seguridad. Como mínimo, de la tabla: CREATE TABLE libros_copia AS SELECT * FROM libros;.
  2. Prueba primero en una copia de la base de datos, nunca directamente en producción.
  3. Comprueba dónde estás conectado. Muchos desastres vienen de ejecutar un DROP en el servidor equivocado.
  4. Cuidado con las tablas grandes. Un ALTER TABLE sobre millones de filas puede tardar minutos y bloquear la tabla mientras tanto.

Consejo: en MySQL, la herramienta mysqldump guarda una base de datos entera en un archivo .sql (con todos sus CREATE TABLE e INSERT) desde la terminal: mysqldump -u root -p biblioteca > copia_biblioteca.sql. Para restaurarla: mysql -u root -p biblioteca < copia_biblioteca.sql. En PostgreSQL, el equivalente es pg_dump.

Migraciones en proyectos reales

En un proyecto de verdad nadie escribe ALTER TABLE a mano en el servidor. Los cambios de estructura se guardan como migraciones: archivos numerados que se ejecutan en orden y se guardan en Git junto al código.

migraciones/
  001_crear_libros.sql
  002_crear_socios.sql
  003_anadir_stock_a_libros.sql
  004_crear_prestamos.sql

Cada migración contiene un cambio pequeño (por ejemplo, ALTER TABLE libros ADD COLUMN stock INT NOT NULL DEFAULT 0;). La herramienta anota en una tabla cuáles se han aplicado ya, de modo que cualquier compañero, el servidor de pruebas y el de producción llegan exactamente a la misma estructura. Muchas permiten también una migración “hacia atrás” para deshacer el cambio.

Casi todos los frameworks traen su sistema: Laravel (PHP), Django (Python), Spring con Flyway o Liquibase (Java), Prisma o Knex (Node.js). Lo aprenderás cuando uses uno, pero la idea es siempre la misma: cada cambio de estructura, en un archivo versionado.

Errores frecuentes

  • MODIFY en MySQL sin repetir NOT NULL o DEFAULT: se pierden sin avisar.
  • Añadir una columna NOT NULL sin DEFAULT a una tabla con filas: error en PostgreSQL y SQLite; valores vacíos en MySQL.
  • Confiar en ROLLBACK después de un DROP o TRUNCATE en MySQL: el cambio ya es definitivo.
  • Usar TRUNCATE para borrar “algunas” filas: no admite WHERE. Para eso, DELETE.
  • Renombrar tablas o columnas sin actualizar el código que las usa.
  • Borrar sin copia de seguridad. Siempre, antes, una copia.

Resumen

Quiero…Instrucción
Añadir una columnaALTER TABLE t ADD COLUMN c TIPO
Borrar una columnaALTER TABLE t DROP COLUMN c
Renombrar una columnaALTER TABLE t RENAME COLUMN a TO b
Cambiar el tipo (MySQL)ALTER TABLE t MODIFY COLUMN c TIPO_COMPLETO
Cambiar el tipo (PostgreSQL)ALTER TABLE t ALTER COLUMN c TYPE TIPO
Añadir una restricciónALTER TABLE t ADD CONSTRAINT nombre ...
Quitar una restricciónALTER TABLE t DROP CONSTRAINT nombre
Renombrar una tablaALTER TABLE t RENAME TO nuevo o RENAME TABLE (MySQL)
Borrar una tablaDROP TABLE IF EXISTS t
Vaciar una tablaTRUNCATE TABLE t
Borrar una base de datosDROP DATABASE IF EXISTS bd

En la siguiente lección, vistas, aprenderás a guardar consultas con nombre para usarlas como si fueran tablas.

Pon a prueba lo que has aprendido

[SQL] En MySQL, la columna titulo es VARCHAR(100) NOT NULL. ¿Qué pasa tras ejecutar esto?
ALTER TABLE libros MODIFY COLUMN titulo VARCHAR(200);

[SQL] Quieres borrar solo los pedidos anteriores a 2026 y dejar el resto. ¿Qué usas?

[SQL] La tabla libros tiene 2 filas. ¿Qué valor de stock tienen después de este ALTER TABLE?
ALTER TABLE libros ADD COLUMN stock INT NOT NULL DEFAULT 1;

[SQL] En MySQL ejecutas BEGIN; DROP TABLE pruebas; ROLLBACK;. ¿Qué pasa con la tabla?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.