Diseñar tablas
💡 🎯 Al terminar esta lección podrás…
- Crear tablas con
CREATE TABLE, eligiendo tipos y restricciones. - Usar
PRIMARY KEY,NOT NULL,UNIQUE,DEFAULTyCHECK. - Modificar y eliminar tablas con
ALTER TABLEyDROP TABLE. - Aplicar la normalización básica: «un dato, un lugar».
1.CREATE TABLE: crear una tabla
Hasta ahora usábamos una tabla ya creada. CREATE TABLE define una nueva: su nombre y, para cada columna, su nombre, su tipo y sus restricciones.
CREATE TABLE nombre_tabla (
columna1 TIPO restricciones,
columna2 TIPO restricciones,
...
);
Un ejemplo completo: la tabla de clientes de la tienda.
CREATE TABLE clientes (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
edad INTEGER CHECK (edad >= 18),
pais TEXT DEFAULT 'Perú'
);
Lee cada línea como una frase:
id INTEGER PRIMARY KEY: número entero que identifica cada cliente.nombre TEXT NOT NULL: texto obligatorio.email TEXT NOT NULL UNIQUE: obligatorio y no repetible entre clientes.edad INTEGER CHECK (edad >= 18): si se indica, debe ser 18 o más.pais TEXT DEFAULT 'Perú': si no se indica, vale'Perú'.
Prueba a insertar un cliente indicando solo lo necesario:
INSERT INTO clientes (nombre, email, edad)
VALUES ('Ana Torres', 'ana@correo.com', 25);
SELECT * FROM clientes;
Resultado esperado (el país se rellenó solo)
| id | nombre | edad | pais | |
|---|---|---|---|---|
| 1 | Ana Torres | ana@correo.com | 25 | Perú |
📌 Nota · Una columna por cada dato simple
Cada columna guarda un solo dato de un solo tipo. Si te encuentras guardando listas separadas por comas dentro de una celda, es señal de que necesitas otra tabla (más abajo, en normalización).
2.Las restricciones de columna
| Restricción | Qué garantiza | Ejemplo |
|---|---|---|
PRIMARY KEY | Identifica la fila: único y nunca vacío | id INTEGER PRIMARY KEY |
NOT NULL | La columna siempre debe tener valor | nombre TEXT NOT NULL |
UNIQUE | No hay dos filas con el mismo valor | email TEXT UNIQUE |
DEFAULT | Valor que se usa si no se indica ninguno | pais TEXT DEFAULT 'Perú' |
CHECK | Una condición que el valor debe cumplir | CHECK (precio > 0) |
FOREIGN KEY | El valor debe existir en otra tabla (lección 5) | REFERENCES clientes(id) |
Si una instrucción rompe una restricción, la base de datos la rechaza. Estos son los mensajes reales de SQLite para nuestra tabla clientes:
INSERT INTO clientes (nombre, email)
VALUES ('Otra Ana', 'ana@correo.com');
UNIQUE constraint failed: clientes.email
INSERT INTO clientes (nombre, email, edad)
VALUES ('Luis', 'luis@correo.com', 15);
CHECK constraint failed: edad >= 18
INSERT INTO clientes (email)
VALUES ('sin-nombre@correo.com');
NOT NULL constraint failed: clientes.nombre
💡 Consejo · Deja que la base de datos valide
Valida también en tu aplicación para dar mensajes amables, pero no confíes solo en eso: las restricciones de la tabla son la última defensa y protegen los datos aunque haya un error en el código o alguien escriba directamente en la base.
Identificadores automáticos
Como vimos, las claves id suelen numerarse solas. La sintaxis depende del motor:
| Motor | Cómo declararlo |
|---|---|
| SQLite | id INTEGER PRIMARY KEY (se autonumera). Añadir AUTOINCREMENT evita reutilizar ids borrados, pero rara vez hace falta. |
| PostgreSQL | id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY (o el clásico SERIAL PRIMARY KEY). |
3.Elegir la clave primaria
Una buena clave primaria cumple tres cosas: es única, nunca está vacía y no cambia. Por eso suele usarse una clave sustituta (surrogate key): un número id sin significado propio, generado por la base de datos.
| Opción | Problema o ventaja |
|---|---|
| El email como clave | La gente cambia de correo; habría que actualizar todo lo que lo use |
| El nombre como clave | Se repite («Ana Torres» puede haber dos) y puede tener erratas |
Un id numérico | Nunca cambia, es corto y rápido de comparar |
Esto no impide que el correo siga siendo único: se consigue con UNIQUE, como en la tabla clientes. Clave primaria (identifica) y valor único (evita repetidos) son dos ideas distintas.
4.Modificar y eliminar tablas
El diseño cambia con el tiempo. Estas instrucciones funcionan en SQLite y en PostgreSQL:
-- Añadir una columna
ALTER TABLE clientes ADD COLUMN telefono TEXT;
-- Renombrar una tabla
ALTER TABLE clientes RENAME TO usuarios;
-- Eliminar la tabla y TODOS sus datos
DROP TABLE usuarios;
-- Variantes seguras: no fallan si ya existe / si no existe
CREATE TABLE IF NOT EXISTS clientes (id INTEGER PRIMARY KEY);
DROP TABLE IF EXISTS clientes;
⚠️ Importante · DROP TABLE no pregunta ni se deshace fácilmente
Elimina la tabla completa con todos sus datos. Hazlo solo si estás seguro y con copia de seguridad (lección 8). Además, al añadir una columna a una tabla con filas, esta se rellena con NULL (o con su DEFAULT): si pones NOT NULL necesitas un DEFAULT.
📌 Nota · SQLite modifica menos que PostgreSQL
SQLite solo permite cambios sencillos con ALTER TABLE (añadir, renombrar y, desde la versión 3.35, eliminar columnas). Cambiar el tipo de una columna o añadir restricciones a una existente suele exigir crear una tabla nueva y copiar los datos. PostgreSQL permite mucho más, por ejemplo ALTER TABLE t ALTER COLUMN c TYPE ….
5.Normalización básica: un dato, un lugar
Diseñar bien una tabla es decidir qué datos van en cada tabla. La normalización es un conjunto de reglas para evitar la repetición y los errores que causa. Veamos un diseño ingenuo: todo en una sola tabla de pedidos.
| pedido | cliente | productos | total | |
|---|---|---|---|---|
| 1 | Ana Torres | ana@correo.com | Teclado, Ratón | 165.50 |
| 2 | Ana Torres | ana@correo.com | Webcam | 150.00 |
| 3 | Luis Pérez | luis@correo.com | Monitor 4K, Cable HDMI | 914.00 |
Problemas de esta tabla:
- Varios valores en una celda (
Teclado, Ratón): no se puede buscar, contar ni sumar bien. Rompe la primera forma normal (1FN): cada celda debe tener un solo valor. - Datos repetidos: el nombre y el correo de Ana están en dos filas. Si cambia de correo, hay que corregirlo en todas, y si se olvida una, los datos se contradicen.
- Datos que dependen de otra cosa: el nombre y el correo dependen del cliente, no del pedido. Es lo que corrigen la 2FN y 3FN: cada dato debe depender de la clave de su tabla y de nada más.
La solución es repartir los datos en tablas, cada una sobre una sola cosa, enlazadas con claves:
| Tabla | Columnas | Guarda |
|---|---|---|
clientes | id, nombre, email | Cada cliente, una sola vez |
productos | id, nombre, categoria, precio, stock | Cada producto, una sola vez |
pedidos | id, cliente_id, fecha | Cada pedido y quién lo hizo |
lineas_pedido | pedido_id, producto_id, cantidad | Qué productos tiene cada pedido, y cuántos |
💡 Consejo · Regla práctica
Si repites el mismo dato en muchas filas, o metes una lista en una celda, crea otra tabla. «Un dato, un lugar»: cada hecho se guarda una sola vez y se enlaza donde haga falta. En la próxima lección usaremos JOIN para volver a juntar estas tablas al consultar.
📌 Nota · No hace falta obsesionarse
Las formas normales llegan hasta la 5FN, pero en la práctica cumplir hasta la tercera da diseños limpios para casi cualquier aplicación. En ocasiones se duplica un dato a propósito para acelerar consultas (desnormalizar), pero se hace con criterio, no por desorden.
6.Buenos nombres y hábitos
- Tablas en plural y en minúsculas:
clientes,productos. - Columnas en minúsculas con guion bajo:
fecha_nacimiento,cliente_id. Sin espacios ni tildes ni la letra «ñ», para evitar tener que usar comillas. - Evita palabras reservadas como
order,userogroupcomo nombre de tabla (por eso se usapedidosyusuarios). - Clave foránea:
otra_tabla_en_singular_id, comocliente_id. - Guarda el script de creación en un archivo
.sql(comotienda.sql) y versiónalo con Git: es la documentación viva de tu diseño.
7.Pruébalo: laboratorio de restricciones
Activa o desactiva restricciones en la tabla usuarios y mira cómo cambia la instrucción CREATE TABLE. Luego intenta insertar filas y observa cuándo la base de datos las rechaza. Hay ya un usuario guardado: Ana con id 1 y el correo ana@mail.com. (Es una simulación: usa los mismos mensajes de error que SQLite.)
🧪 Ejemplo · Experimentos
1) Con todo activado, inserta con el correo ana@mail.com: UNIQUE lo rechaza. 2) Desactiva UNIQUE y repite: ahora entra y tendrás dos usuarios con el mismo correo. 3) Deja el nombre vacío con NOT NULL activo y sin él. 4) Pon edad 15 con y sin CHECK. 5) Escribe 1 en id con y sin PRIMARY KEY.
8.Errores frecuentes
- Olvidar
NOT NULLen columnas obligatorias: acaban entrando filas sin nombre o sin correo. - Usar el email o el nombre como clave primaria en lugar de un
id. - Guardar listas separadas por comas en una celda (
'Teclado, Ratón'): crea otra tabla. - Repetir los datos del cliente en cada pedido: si cambian, quedan versiones contradictorias.
- Coma sobrante después de la última columna en
CREATE TABLE, o falta de coma entre dos columnas: error de sintaxis. - Ejecutar
CREATE TABLEdos veces: la segunda falla porque la tabla ya existe; usaIF NOT EXISTSoDROP TABLE IF EXISTSantes. - Usar
DROP TABLEsin copia: elimina la tabla y todo su contenido. - Copiar sintaxis de un motor a otro (
AUTOINCREMENT,SERIAL…): comprueba la documentación del motor que usas.
📒 Apuntes para llevar
CREATE TABLE nombre (columna TIPO restricciones, …);define la estructura: columnas, tipos y reglas.PRIMARY KEYidentifica la fila;NOT NULLobliga a dar valor;UNIQUEimpide repetidos;DEFAULTda un valor inicial;CHECKexige una condición.- Usa un
idnumérico como clave primaria yUNIQUEpara datos que no deben repetirse (como el email). ALTER TABLEmodifica una tabla;DROP TABLEla elimina con todos sus datos.- Normalizar es repartir los datos en tablas: un dato, un lugar; sin listas en una celda ni datos repetidos.
- La sintaxis de los identificadores automáticos difiere:
INTEGER PRIMARY KEYen SQLite yGENERATED ALWAYS AS IDENTITYen PostgreSQL. - Nombres en minúsculas con guion bajo, tablas en plural y claves foráneas como
cliente_id.
9.Glosario
| Término | Significado |
|---|---|
| CREATE TABLE | Instrucción que crea una tabla indicando sus columnas, tipos y restricciones. |
| PRIMARY KEY | Restricción que identifica cada fila: valor único y nunca vacío. |
| NOT NULL | Restricción que obliga a que la columna siempre tenga valor. |
| UNIQUE | Restricción que impide valores repetidos en una columna. |
| CHECK | Restricción con una condición que cada valor debe cumplir. |
| Clave sustituta | Identificador sin significado propio (un id numérico) que genera la base de datos. |
| Normalización | Proceso de organizar las tablas para evitar datos repetidos y contradictorios. |
| Primera forma normal (1FN) | Cada celda contiene un único valor, nunca una lista. |
🧠 Comprueba lo aprendido
Elige una respuesta en cada pregunta. Verás al instante si acertaste y por qué.
¿Qué restricción impide que dos clientes tengan el mismo email?
UNIQUE rechaza valores repetidos. NOT NULL solo obliga a que haya valor.
¿Qué significa precio NUMERIC CHECK (precio > 0)?
CHECK exige que la condición se cumpla al guardar el valor.
¿Por qué es mala idea guardar 'Teclado, Ratón' en una sola celda?
Una celda debe tener un único valor (1FN). Las listas se guardan como filas en una tabla relacionada.
¿Qué hace DROP TABLE clientes;?
DROP TABLE borra la tabla entera. Para vaciar solo las filas se usa DELETE FROM.
¿Cuál es la mejor clave primaria para clientes?
Un id sustituto es único, no cambia y no depende de datos que puedan variar. El email se protege con UNIQUE.
Tu progreso se guarda en este navegador y se refleja en el índice del curso.
💡 🚀 ¡Ahora te toca!
La práctica fija lo aprendido. Pasa al ejercicio de esta lección.