Consultas SQL para gestión de hoteles, personal y actividades
Resumen
El documento recoge ejercicios prácticos de SQL aplicados a la gestión de alojamientos, personal, habitaciones y actividades. Cada ejercicio incluye el enunciado y una consulta resuelta con subconsultas, JOIN, GROUP BY, HAVING, INTERSECT y funciones de agregación. Sirve para aprender a consultar bases de datos relacionales de un caso práctico de hotelería.
Puntos clave
- Las subconsultas permiten encontrar, por ejemplo, al empleado con la fecha de alta más temprana mediante MIN(fecAlta).
- JOIN combina las tablas Personal, Trabajar, Alojamientos y Habitaciones a través de sus claves (idTrabajador, idAlojamiento).
- GROUP BY con COUNT o AVG agrupa resultados por alojamiento, por ejemplo para contar habitaciones sin baño o calcular precios medios.
- HAVING filtra grupos después de agrupar, como los alojamientos con más de dos contratos de personal.
- INTERSECT devuelve los alojamientos comunes a dos empleados, usado para cruzar los lugares donde trabajaron Luis Martínez y Susana García.
- NOT IN excluye registros que aparecen en una subconsulta, como los empleados sin historial laboral.
Consultas SQL Esenciales para Gestión de Hoteles y Personal
Este documento presenta una serie de ejercicios prácticos de SQL, diseñados para resolver problemas comunes en la gestión de bases de datos relacionadas con alojamientos, personal, habitaciones y actividades. Cada punto incluye el enunciado del problema y su correspondiente consulta SQL optimizada.
11. Empleado con la fecha de alta más temprana
Obtener todos los datos del empleado que comenzó a trabajar en la empresa en la fecha más antigua.
SELECT *
FROM Personal
WHERE idTrabajador = (
SELECT tra.idTrabajador
FROM Trabajar tra
WHERE tra.fecAlta = (
SELECT MIN(tra.fecAlta)
FROM Trabajar tra
)
);12. Historial laboral completo de cada empleado
Generar un historial laboral detallado para cada empleado, mostrando su nombre, el alojamiento donde trabajó, y las fechas de alta y baja. La salida debe estar ordenada por el nombre del empleado y, para cada empleado, por la fecha de incorporación en el alojamiento.
SELECT
per.nombre AS NombreEmpleado,
alo.nombre AS NombreAlojamiento,
tra.fecAlta AS FechaAlta,
tra.fecBaja AS FechaBaja
FROM Personal per
JOIN Trabajar tra ON per.idTrabajador = tra.idTrabajador
JOIN Alojamientos alo ON tra.idAlojamiento = alo.idAlojamiento
ORDER BY
NombreEmpleado,
FechaAlta;13. Conteo de habitaciones sin baño por alojamiento
Determinar el número de habitaciones que no disponen de baño en cada uno de los alojamientos.
SELECT
alo.nombre AS NombreAlojamiento,
COUNT(*) AS "Número de habitaciones sin baño"
FROM Habitaciones hab
JOIN Alojamientos alo ON hab.idAlojamiento = alo.idAlojamiento
WHERE hab.baño = 'n'
GROUP BY alo.nombre;14. Alojamientos con más de dos contratos de personal
Identificar los alojamientos que han gestionado más de dos contratos de personal.
SELECT alo.nombre AS NombreAlojamiento
FROM Trabajar tra
JOIN Alojamientos alo ON tra.idAlojamiento = alo.idAlojamiento
GROUP BY alo.nombre
HAVING COUNT(*) > 2;15. Precio medio de habitaciones sin baño por alojamiento
Calcular el precio medio de las habitaciones que no disponen de baño para cada alojamiento.
SELECT
alo.nombre AS NombreAlojamiento,
AVG(hab.precio) AS "Precio Medio"
FROM Habitaciones hab
JOIN Alojamientos alo ON hab.idAlojamiento = alo.idAlojamiento
WHERE hab.baño = 'n'
GROUP BY alo.nombre;16. Alojamientos compartidos por Luis Martínez y Susana García
Encontrar los alojamientos donde tanto el Sr. Luis Martínez como la Sra. Susana García han trabajado, utilizando operaciones de intersección.
SELECT alo.nombre AS NombreAlojamiento
FROM Trabajar tra
JOIN Alojamientos alo ON tra.idAlojamiento = alo.idAlojamiento
WHERE tra.idTrabajador = (
SELECT idTrabajador
FROM Personal
WHERE nombre LIKE '%Luis Martínez%'
)
INTERSECT
SELECT alo.nombre
FROM Trabajar tra
JOIN Alojamientos alo ON tra.idAlojamiento = alo.idAlojamiento
WHERE tra.idTrabajador = (
SELECT idTrabajador
FROM Personal
WHERE nombre LIKE '%Susana García%'
);
17. Actividades con el nivel de dificultad más bajo
Mostrar el nombre y la descripción de todas aquellas actividades cuyo nivel de dificultad sea el más sencillo (mínimo).
SELECT *
FROM Actividades
WHERE dificultad = (
SELECT MIN(dificultad)
FROM Actividades
);
18. Habitaciones por tipo y alojamiento
Listar el nombre de los alojamientos junto con el número de habitaciones que poseen de cada tipo. La salida debe estar ordenada primero por el tipo de habitación (descendente) y luego por el número de habitaciones por establecimiento (descendente).
SELECT
COUNT(*) AS NumHabitaciones,
alo.nombre AS NombreAlojamiento,
hab.tipo AS TipoHabitacion
FROM Habitaciones hab
JOIN Alojamientos alo ON hab.idAlojamiento = alo.idAlojamiento
GROUP BY
alo.nombre,
hab.tipo
ORDER BY
TipoHabitacion DESC,
NumHabitaciones DESC;19. Empleados sin historial laboral
Identificar a los empleados que nunca han registrado un contrato de trabajo en ningún alojamiento.
SELECT *
FROM Personal per
WHERE per.idTrabajador NOT IN (
SELECT DISTINCT idTrabajador
FROM Trabajar
);
20. Alojamientos con actividades de máxima dificultad
Obtener todos los datos de los alojamientos que ofrecen al menos una actividad con el nivel de dificultad más alto.
SELECT *
FROM Alojamientos
WHERE idAlojamiento IN (
SELECT idAlojamiento
FROM Organizar
WHERE idActividad = (
SELECT idActividad
FROM Actividades
WHERE dificultad = (
SELECT MAX(dificultad)
FROM Actividades
)
)
);
Nota: La consulta original usaba <=any, que es equivalente a IN para este caso. Se ha simplificado para mayor claridad.
21. Empleados actuales y datos de alojamientos en Castropol
Obtener el nombre y teléfono de los alojamientos, junto con el nombre de sus empleados actuales, para aquellos alojamientos situados en el concejo de Castropol.
SELECT
per.nombre AS NombreEmpleado,
alo.nombre AS NombreAlojamiento,
alo.telefono AS TelefonoAlojamiento
FROM Alojamientos alo
JOIN Trabajar tra ON alo.idAlojamiento = tra.idAlojamiento
JOIN Personal per ON tra.idTrabajador = per.idTrabajador
WHERE alo.direccion LIKE '%castropol%'
AND tra.fecBaja IS NULL;22. Estadísticas de precios de habitaciones (general)
Calcular el precio mínimo, precio máximo y precio medio de todas las habitaciones en todos los alojamientos.
SELECT
AVG(precio) AS precio_medio,
MIN(precio) AS precio_minimo,
MAX(precio) AS precio_maximo
FROM Habitaciones;23. Estadísticas de precios de habitaciones (formato específico)
Calcular el precio mínimo, precio máximo y precio medio de todas las habitaciones en todos los alojamientos, asegurando que todos los componentes de la lista de selección de salida mantengan el mismo formato decimal que el campo precio.
SELECT
CAST(AVG(precio) AS DECIMAL(10,2)) AS precio_medio,
MIN(precio) AS precio_minimo,
MAX(precio) AS precio_maximo
FROM Habitaciones;Nota: Se ha ajustado el tipo DECIMAL(5,2) a DECIMAL(10,2) para permitir valores de precio más grandes si fuera necesario, manteniendo dos decimales.
24. Número de actividades por alojamiento (semanal)
Obtener el nombre de cada alojamiento y el número total de actividades que ofrece a lo largo de la semana, ordenando la salida por el número de actividades en sentido descendente.
SELECT
alo.nombre AS NombreAlojamiento,
COUNT(org.idActividad) AS "Número de Actividades"
FROM Alojamientos alo
JOIN Organizar org ON alo.idAlojamiento = org.idAlojamiento
GROUP BY alo.nombre
ORDER BY "Número de Actividades" DESC;25. Alojamientos sin actividades de máxima dificultad
Identificar los alojamientos que no ofrecen ninguna actividad con el nivel de dificultad más alto.
SELECT *
FROM Alojamientos
WHERE idAlojamiento NOT IN (
SELECT idAlojamiento
FROM Organizar
WHERE idActividad = (
SELECT idActividad
FROM Actividades
WHERE dificultad = (
SELECT MAX(dificultad)
FROM Actividades
)
)
);
Nota: La consulta original usaba >all, que es equivalente a NOT IN para este caso. Se ha simplificado para mayor claridad.
Descargar
PDF Word HTMLPreguntas frecuentes
¿Cómo se obtiene el empleado con la fecha de alta más antigua en SQL?
Se usa una subconsulta con MIN(tra.fecAlta) para obtener la fecha más temprana y después se busca el idTrabajador asociado a esa fecha. El resultado final se muestra con SELECT * sobre la tabla Personal.
¿Cuál es la diferencia entre WHERE y HAVING en estas consultas?
WHERE filtra filas antes de agrupar, mientras que HAVING filtra grupos después del GROUP BY. Por ejemplo, HAVING COUNT(*) > 2 selecciona solo los alojamientos con más de dos contratos.
¿Qué hace el operador INTERSECT en la consulta de alojamientos compartidos?
INTERSECT devuelve solo los resultados que aparecen en ambas consultas, es decir, los alojamientos donde han trabajado tanto Luis Martínez como Susana García.
Ponte a prueba
Tarjetas de repaso
Pulsa una tarjeta para ver la respuesta.
Test
Preguntas generadas con IA a partir del documento: comprueba las respuestas con el texto.