10 ejemplos de bases de datos con tablas y consultas
Una base de datos organiza información para consultarla y actualizarla. Una nómina relaciona empleados con puestos; una biblioteca conecta ejemplares con préstamos. Estos ejemplos muestran los datos, la pregunta que queremos responder y el resultado de una consulta.
Ejemplos de bases de datos explicados
Todos los nombres, importes y registros son ficticios. Los nueve primeros casos usan tablas relacionales; el último presenta una estructura documental. Las tablas muestran los datos iniciales y, después del SQL, el resultado esperado.
Puedes leer los resultados sin programar. Para ejecutar un caso, abre «Preparar tablas y datos» y sigue las indicaciones de prueba. Las tablas anchas se pueden desplazar horizontalmente.
Una empresa ficticia guarda los puestos por separado y asigna un puesto a cada empleado. El sueldo del ejercicio pertenece al empleado: compartir puesto no obliga a cobrar lo mismo.
puestos
id
nombre
1
Soporte
2
Ventas
empleados
id
nombre
puesto_id
sueldo
10
Ana
1
1200
11
Luis
1
1400
12
Eva
2
1300
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE puestos (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL
);
CREATE TABLE empleados (
id INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
puesto_id INTEGER NOT NULL REFERENCES puestos(id),
sueldo INTEGER NOT NULL CHECK(sueldo >= 0)
);
INSERT INTO puestos VALUES (1, 'Soporte'), (2, 'Ventas');
INSERT INTO empleados VALUES
(10, 'Ana', 1, 1200), (11, 'Luis', 1, 1400),
(12, 'Eva', 2, 1300);
Consulta
SELECT e.nombre, p.nombre AS puesto, e.sueldo
FROM empleados AS e
JOIN puestos AS p ON p.id = e.puesto_id
ORDER BY e.id;
Resultado de la consulta
nombre
puesto
sueldo
Ana
Soporte
1200
Luis
Soporte
1400
Eva
Ventas
1300
La clave foránea puesto_id apunta a un puesto existente. JOIN combina los datos relacionados para mostrar el nombre del puesto junto al empleado. Los importes son unidades monetarias enteras de un modelo didáctico; no se calcula una nómina legal completa.
Qué puede fallar: Asignar puesto_id = 99 se rechaza porque no existe ese puesto. La restricción no comprueba si el puesto elegido es realmente el de la persona.
EJEMPLO 02
Contabilidad: cuentas, subcuentas y movimientos
Agrupar y calcular
Este registro simplificado clasifica movimientos por subcuenta. No representa un sistema completo de asientos por partida doble.
cuentas
id
nombre
grupo
1
Ingresos
Resultados
subcuentas
id
cuenta_id
nombre
11
1
Ventas de libros
12
1
Talleres
movimientos
id
subcuenta_id
importe
1
11
100
2
11
50
3
12
80
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE cuentas (
id INTEGER PRIMARY KEY, nombre TEXT NOT NULL,
grupo TEXT NOT NULL
);
CREATE TABLE subcuentas (
id INTEGER PRIMARY KEY,
cuenta_id INTEGER NOT NULL REFERENCES cuentas(id),
nombre TEXT NOT NULL
);
CREATE TABLE movimientos (
id INTEGER PRIMARY KEY,
subcuenta_id INTEGER NOT NULL REFERENCES subcuentas(id),
importe INTEGER NOT NULL
);
INSERT INTO cuentas VALUES (1, 'Ingresos', 'Resultados');
INSERT INTO subcuentas VALUES
(11, 1, 'Ventas de libros'), (12, 1, 'Talleres');
INSERT INTO movimientos VALUES
(1, 11, 100), (2, 11, 50), (3, 12, 80);
Consulta
SELECT s.nombre, SUM(m.importe) AS total
FROM subcuentas AS s
JOIN movimientos AS m ON m.subcuenta_id = s.id
GROUP BY s.id, s.nombre
ORDER BY s.id;
Resultado de la consulta
nombre
total
Ventas de libros
150
Talleres
80
La consulta obtiene un dato calculado: el total de cada subcuenta. Los movimientos se conservan por separado para poder explicar de dónde procede cada suma. El grupo contable se registra en cuentas; no se repite en cada movimiento.
Qué puede fallar: Una suma correcta puede partir de importes incorrectos. La base guarda lo que se registra: se necesitan revisión, respaldo documental y reglas de acceso.
EJEMPLO 03
Banca: clientes, cuentas y una transferencia
Confirmar cambios juntos
Dos cuentas ficticias pertenecen a clientes distintos. El ejercicio transfiere 40 unidades de A a B, sin comisiones y dentro de una misma base de datos.
clientes
id
nombre
1
Ana
2
Luis
cuentas
id
cliente_id
saldo
A
1
500
B
2
100
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE clientes (
id INTEGER PRIMARY KEY, nombre TEXT NOT NULL
);
CREATE TABLE cuentas (
id TEXT PRIMARY KEY NOT NULL,
cliente_id INTEGER NOT NULL REFERENCES clientes(id),
saldo INTEGER NOT NULL CHECK(saldo >= 0)
);
INSERT INTO clientes VALUES (1, 'Ana'), (2, 'Luis');
INSERT INTO cuentas VALUES ('A', 1, 500), ('B', 2, 100);
Transferencia: ejecutar una vez
BEGIN;
UPDATE cuentas SET saldo = saldo - 40 WHERE id = 'A';
UPDATE cuentas SET saldo = saldo + 40 WHERE id = 'B';
COMMIT;
Consulta
SELECT id, saldo FROM cuentas ORDER BY id;
Resultado de la consulta
id
saldo
A
460
B
140
La transacción agrupa ambas modificaciones. Tras COMMIT, los saldos suman 600, igual que antes. Si se decide cancelar antes de confirmar, ROLLBACK deshace los cambios de esa transacción. En una aplicación también hay que validar que las dos cuentas existen y comprobar cuántas filas se actualizan. [3]
Qué puede fallar: Una transacción no reconoce por sí sola al destinatario correcto ni autoriza al usuario. Ante un error, la aplicación debe manejarlo y cancelar cuando corresponda; no debe continuar hasta confirmar una operación parcial.
EJEMPLO 04
Biblioteca: libros prestados y devueltos
Consultar un valor ausente
Cada ejemplar físico tiene un identificador. Un préstamo sin fecha de devolución todavía está abierto en este registro.
ejemplares
id
titulo
1
Atlas del cielo
2
Atlas del cielo
prestamos
id
ejemplar_id
devuelto
10
1
NULL
11
2
2026-09-30
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE ejemplares (
id INTEGER PRIMARY KEY, titulo TEXT NOT NULL
);
CREATE TABLE prestamos (
id INTEGER PRIMARY KEY,
ejemplar_id INTEGER NOT NULL REFERENCES ejemplares(id),
devuelto TEXT
);
INSERT INTO ejemplares VALUES
(1, 'Atlas del cielo'), (2, 'Atlas del cielo');
INSERT INTO prestamos VALUES
(10, 1, NULL), (11, 2, '2026-09-30');
Consulta
SELECT p.id AS prestamo, e.titulo
FROM prestamos AS p
JOIN ejemplares AS e ON e.id = p.ejemplar_id
WHERE p.devuelto IS NULL
ORDER BY p.id;
Resultado de la consulta
prestamo
titulo
10
Atlas del cielo
IS NULL busca ausencia de valor: no equivale a una cadena vacía ni a la palabra «NULL». Dos ejemplares pueden compartir título; por eso el título no se usa como identificador de la copia.
Qué puede fallar: La clave foránea acredita que el ejemplar existe, pero este esquema todavía no impide dos préstamos abiertos para la misma copia. Esa regla requiere otro control.
EJEMPLO 05
Inventario: existencias por almacén
Una clave de dos columnas
El producto A está en dos almacenes. Lo que debe ser único es la combinación de producto y almacén.
existencias
producto
almacen
cantidad
A
Norte
8
A
Sur
5
B
Norte
3
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE existencias (
producto TEXT NOT NULL,
almacen TEXT NOT NULL,
cantidad INTEGER NOT NULL CHECK(cantidad >= 0),
PRIMARY KEY(producto, almacen)
);
INSERT INTO existencias VALUES
('A', 'Norte', 8), ('A', 'Sur', 5), ('B', 'Norte', 3);
Consulta
SELECT producto, SUM(cantidad) AS unidades
FROM existencias
GROUP BY producto
ORDER BY producto;
Resultado de la consulta
producto
unidades
A
13
B
3
La clave primaria compuesta permite repetir A en almacenes distintos, pero no registrar dos filas A–Norte. La consulta suma existencias por producto, sin perder el detalle de ubicación.
Qué puede fallar: CHECK rechaza cantidades negativas, pero no detecta que alguien introdujo 80 cuando había 8. La validez del formato y la exactitud del dato son cosas diferentes.
EJEMPLO 06
Escuela: alumnos inscritos en cursos
Una relación de muchos a muchos
Un alumno puede cursar varias materias y un curso puede tener varios alumnos. Una tabla intermedia registra cada inscripción.
alumnos
id
nombre
1
Eva
2
Leo
cursos
id
nombre
10
Arte
20
Ciencias
inscripciones
alumno_id
curso_id
1
10
1
20
2
20
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE alumnos (
id INTEGER PRIMARY KEY, nombre TEXT NOT NULL
);
CREATE TABLE cursos (
id INTEGER PRIMARY KEY, nombre TEXT NOT NULL
);
CREATE TABLE inscripciones (
alumno_id INTEGER NOT NULL REFERENCES alumnos(id),
curso_id INTEGER NOT NULL REFERENCES cursos(id),
PRIMARY KEY(alumno_id, curso_id)
);
INSERT INTO alumnos VALUES (1, 'Eva'), (2, 'Leo');
INSERT INTO cursos VALUES (10, 'Arte'), (20, 'Ciencias');
INSERT INTO inscripciones VALUES (1, 10), (1, 20), (2, 20);
Consulta
SELECT a.nombre AS alumno, c.nombre AS curso
FROM inscripciones AS i
JOIN alumnos AS a ON a.id = i.alumno_id
JOIN cursos AS c ON c.id = i.curso_id
ORDER BY a.id, c.id;
Resultado de la consulta
alumno
curso
Eva
Arte
Eva
Ciencias
Leo
Ciencias
La tabla intermedia representa una relación. Esto muestra por qué no toda tabla tiene que representar una entidad como «persona» o «curso». Cada fila combina dos claves.
Qué puede fallar: Inscribir de nuevo a Eva en Arte genera un conflicto de clave primaria. Si se necesitan cursos por año, el modelo debe distinguir también las ediciones o periodos.
EJEMPLO 07
Reservas de una sala
Unicidad de una franja
Un centro cultural reserva una sala mediante franjas fijas de una hora. La fecha y la hora se guardan con el mismo formato.
reservas
id
sala
inicio
1
A
2026-10-12 10:00
2
A
2026-10-12 11:00
3
B
2026-10-12 10:00
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE reservas (
id INTEGER PRIMARY KEY,
sala TEXT NOT NULL,
inicio TEXT NOT NULL,
UNIQUE(sala, inicio)
);
INSERT INTO reservas VALUES
(1, 'A', '2026-10-12 10:00'),
(2, 'A', '2026-10-12 11:00'),
(3, 'B', '2026-10-12 10:00');
Consulta
SELECT sala, inicio FROM reservas
WHERE inicio = '2026-10-12 10:00'
ORDER BY sala;
Resultado de la consulta
sala
inicio
A
2026-10-12 10:00
B
2026-10-12 10:00
UNIQUE(sala, inicio) impide repetir la misma franja para la misma sala. Reservar otra sala a esa hora sí es válido.
Qué puede fallar: Esta restricción no valida fechas ni detecta cualquier solapamiento. Si las duraciones fueran variables, dos inicios distintos podrían superponerse y haría falta otra regla.
EJEMPLO 08
Tienda: pedidos y líneas de compra
Calcular un total sin repetir datos del pedido
Un pedido reúne varias líneas. El precio de cada línea es el acordado al comprar, no necesariamente el precio actual del catálogo.
pedidos
id
cliente
50
Mara
lineas
pedido_id
numero
cantidad
precio_cent
50
1
2
300
50
2
1
450
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE pedidos (
id INTEGER PRIMARY KEY, cliente TEXT NOT NULL
);
CREATE TABLE lineas (
pedido_id INTEGER NOT NULL REFERENCES pedidos(id),
numero INTEGER NOT NULL,
cantidad INTEGER NOT NULL CHECK(cantidad > 0),
precio_cent INTEGER NOT NULL CHECK(precio_cent >= 0),
PRIMARY KEY(pedido_id, numero)
);
INSERT INTO pedidos VALUES (50, 'Mara');
INSERT INTO lineas VALUES (50, 1, 2, 300), (50, 2, 1, 450);
Consulta
SELECT pedido_id,
SUM(cantidad * precio_cent) AS total_cent
FROM lineas
GROUP BY pedido_id
ORDER BY pedido_id;
Resultado de la consulta
pedido_id
total_cent
50
1050
El total es 1.050 centavos, equivalentes a 10,50 unidades monetarias en una moneda de cien centavos por unidad. Guardar enteros evita representar esos importes mediante aproximaciones de coma flotante. Aquí no se añaden impuestos ni descuentos.
Qué puede fallar: Recalcular una venta antigua con el precio actual alteraría su historia. Reducir redundancia no significa borrar datos que describen condiciones distintas.
EJEMPLO 09
Estación meteorológica
Datos ordenados en el tiempo
Una estación registra tres temperaturas. El instante es parte de la identidad de cada lectura.
lecturas
estacion
instante
temperatura
N
2026-10-01 08:00
18
N
2026-10-01 09:00
20
N
2026-10-01 10:00
22
Preparar tablas y datos
Ejecuta este bloque en una base nueva y vacía para este caso.
PRAGMA foreign_keys = ON;
CREATE TABLE lecturas (
estacion TEXT NOT NULL,
instante TEXT NOT NULL,
temperatura REAL NOT NULL,
PRIMARY KEY(estacion, instante)
);
INSERT INTO lecturas VALUES
('N', '2026-10-01 08:00', 18),
('N', '2026-10-01 09:00', 20),
('N', '2026-10-01 10:00', 22);
Consulta
SELECT estacion, AVG(temperatura) AS media
FROM lecturas
GROUP BY estacion
ORDER BY estacion;
Resultado de la consulta
estacion
media
N
20.0
La media aritmética de estas tres lecturas es 20 °C. Una colección de observaciones temporales también puede guardarse en tablas relacionales; describir los datos como «serie temporal» no obliga a usar un único tipo de gestor.
Qué puede fallar: Tres medidas no bastan para llamar al resultado «temperatura media del día». Para combinar estaciones hay que definir unidades, horarios, zona horaria y tratamiento de lecturas ausentes.
EJEMPLO 10
Catálogo con documentos
Otra forma de organizar los datos
Una base documental puede representar cada producto mediante un documento con campos y estructuras anidadas. Este JSON ilustra un producto ficticio; no es una consulta ejecutada en un gestor.
El color se encuentra dentro de atributos; etiquetas contiene dos elementos. En MongoDB los documentos se almacenan en BSON, una representación binaria con tipos adicionales a JSON. El ejemplo solo ilustra la estructura: no se ha ejecutado en MongoDB. [9]
Qué puede fallar: Una estructura flexible también necesita criterios de validación. Mezclar capacidad_l con capacidad_ml sin definir unidades puede producir comparaciones incorrectas. Documental no significa «sin reglas».
Tablas, claves y relaciones
En una base relacional, una tabla organiza filas y columnas. Una fila es un registro; una columna contiene un atributo como nombre o fecha. Una entidad es un concepto del dominio, como empleado o curso: puede representarse en tablas, pero no son términos intercambiables. Una tabla también puede representar la relación entre entidades, como una inscripción.
Clave primaria: columna o combinación que identifica cada fila. En empleados usamos id; en inscripciones, la pareja alumno_id y curso_id.
Clave foránea: referencia a una clave de otra tabla —o de la misma— para vincular registros y, si se comprueba, impedir referencias inexistentes.
Uno a muchos: un puesto puede corresponder a varios empleados.
Muchos a muchos: alumnos y cursos se conectan mediante inscripciones.
Las restricciones expresan reglas concretas: NOT NULL exige un valor; UNIQUE evita duplicados según las columnas elegidas; CHECK evalúa una condición. Las reglas necesitan un diseño adecuado y no acreditan la veracidad de los datos.
Separar información puede reducir repeticiones innecesarias: el nombre del puesto se guarda una vez y se consulta mediante su clave. Sin embargo, repetir una clave foránea es normal; conservar el precio pactado en una compra también tiene sentido aunque exista otro precio en el catálogo.
Cómo probar el SQL de los ejemplos
Los nueve casos relacionales se ejecutaron con SQLite 3.53.1. Cada caso es independiente: utiliza una base nueva y vacía, ejecuta su bloque de preparación y después su consulta. En banca, ejecuta la transferencia una sola vez antes de consultar.
La preparación crea las tablas e introduce exactamente los registros mostrados.
PRAGMA foreign_keys = ON activa la comprobación de referencias en esa conexión, antes de iniciar una transacción.
SELECT elige columnas; JOIN combina registros relacionados; WHERE filtra; GROUP BY agrupa y ORDER BY fija el orden del resultado.
El resultado debe coincidir con la tabla que sigue al SQL.
Los códigos están escritos para SQLite; no se afirma compatibilidad idéntica con todos los gestores. Los nombres de tablas se repiten entre algunos ejercicios, por eso deben probarse por separado. Repetir una preparación sobre las tablas ya creadas produce un error.
Son esquemas de aprendizaje. No incorporan todos los permisos, controles de fechas, tipos estrictos ni reglas que exigiría una aplicación completa. Las restricciones de los ejemplos cubren únicamente las condiciones declaradas. [1][2]
La base de datos no es el gestor
La base de datos es la colección organizada de datos. El sistema gestor de bases de datos, SGBD o DBMS, es el software que permite definirla, consultar y modificar su contenido y administrar su acceso. Un sistema de nómina utiliza una base de datos, pero incluye además pantallas, reglas y procesos.
Oracle Database, MySQL y SQL Server son ejemplos de gestores con modelo relacional. Informix combina capacidades relacionales con soporte para otros modelos. Progress OpenEdge RDBMS también es un gestor relacional. Estos nombres identifican herramientas, no bases concretas como «los préstamos de una biblioteca» ni una clasificación de popularidad.
Compartir datos entre usuarios es posible mediante el gestor y la aplicación, pero requiere permisos y coordinación de operaciones. Guardar mucha información no garantiza consultas rápidas: también influyen el diseño, los índices, las consultas y los recursos disponibles.
Ocho ejercicios para practicar
En nómina, ¿pueden Ana y Luis compartir puesto_id = 1?
¿Qué sucede al asignar un puesto inexistente con la clave foránea activada?
¿Cuánto suman los movimientos contables mostrados?
¿Por qué buscar préstamos con IS NULL?
¿Cuántas inscripciones aparecen para Eva?
¿UNIQUE(sala, inicio) evita toda reserva superpuesta?
¿Cuál sería el total de dos unidades a 300 centavos y tres a 450?
¿Usar tablas, claves y transacciones garantiza que los datos sean verdaderos?
Ver respuestas explicadas
Sí. La clave primaria de empleados es id; puesto_id puede repetirse porque varios empleados pueden tener el mismo puesto.
La inserción se rechaza. Eso exige que la restricción esté declarada y que su comprobación esté activada en la conexión.
230 unidades: 100 + 50 + 80. Por subcuenta son 150 y 80.
Porque una devolución ausente se representa con NULL. No se compara con = NULL ni con la cadena de texto «NULL».
Dos: Arte y Ciencias. La pareja alumno–curso identifica cada inscripción.
No. Evita repetir la misma pareja; duraciones variables exigen comprobar intervalos y otras condiciones.
1.950 centavos: 2 × 300 + 3 × 450, equivalentes a 19,50 unidades en este modelo.
No. Los controles detectan determinadas inconsistencias; no comprueban automáticamente la veracidad de nombres, importes o decisiones.
Errores frecuentes
Confundir una tabla con toda la base. Una base puede contener varias tablas relacionadas y otros objetos.
Usar un nombre como identificador único sin comprobarlo. Dos personas o dos libros pueden compartir nombre.
Declarar una relación solo de palabra. Una columna con nombre parecido no crea por sí sola una clave foránea.
Suponer que todos los duplicados son errores. Repetir puesto_id es necesario en una relación de uno a muchos; repetir la clave primaria no.
Tratar NULL como cero. La ausencia de un valor tiene un significado diferente.
Confiar en que cualquier error revierte todo. La aplicación debe manejar fallos y cancelar la transacción cuando corresponda.
Afirmar que una base elimina todos los errores. Las restricciones no detectan cualquier dato falso, acceso indebido o decisión equivocada.