Quiz de practica — Parcial 2
Parcial 2 • Dificultad: medium
Pregunta 1 — Verdadero o Falso: modelo relacional (10 pts)
Indica si cada afirmacion es verdadera (V) o falsa (F). Justifica brevemente las falsas.
a) Una superclave siempre es una clave candidata.
b) Si una clave foranea (FK) forma parte de la clave primaria compuesta de su tabla, entonces esa FK no puede ser NULL.
c) NULL = NULL se evalua como TRUE en SQL.
d) La restriccion de integridad referencial dice que una FK debe existir como PK en la tabla referenciada, o ser NULL (si la columna lo permite).
e) DELETE FROM tabla; es una operacion DDL que no se puede deshacer con ROLLBACK.
Ver respuesta
a) FALSO. Es al reves: toda clave candidata es superclave, pero no toda superclave es candidata. Una superclave puede tener atributos redundantes; la clave candidata es una superclave minimal (quitar cualquier atributo rompe la unicidad).
b) VERDADERO. La regla de integridad de entidad dice que ningun componente de la PK puede ser NULL. Si la FK es parte de la PK, hereda esa restriccion y nunca puede ser NULL.
c) FALSO. NULL = NULL se evalua como UNKNOWN, no como TRUE. En la logica de 3 valores de SQL, cualquier comparacion con NULL produce UNKNOWN. Para testear NULLs se usa IS NULL / IS NOT NULL.
d) VERDADERO. Esa es exactamente la definicion de integridad referencial: el valor de la FK debe coincidir con algun valor de PK en la tabla referenciada, o ser NULL (siempre que la columna no tenga restriccion NOT NULL).
e) FALSO. DELETE FROM tabla es DML (Data Manipulation Language), no DDL, y si se puede deshacer con ROLLBACK (dentro de una transaccion). Lo que no se puede deshacer es TRUNCATE TABLE o DROP TABLE, que son DDL.
Pregunta 2 — Identificacion de claves (10 pts)
Dado el siguiente esquema y datos de ejemplo, identifica todas las claves candidatas de la tabla VUELO. Justifica por que cada una es candidata y por que las demas superclaves no lo son.
VUELO(vuelo_id, aerolinea, origen, destino, fecha, hora_salida, avion_id)
| vuelo_id | aerolinea | origen | destino | fecha | hora_salida | avion_id |
|----------|-----------|--------|---------|------------|-------------|----------|
| V001 | AV | BOG | MDE | 2026-03-20 | 08:00 | A320-1 |
| V002 | AV | BOG | MDE | 2026-03-20 | 14:00 | A320-2 |
| V003 | LA | BOG | CLO | 2026-03-20 | 08:00 | B737-1 |
| V004 | AV | BOG | MDE | 2026-03-21 | 08:00 | A320-1 |
| V005 | LA | MDE | BOG | 2026-03-20 | 10:00 | B737-1 |
Restricciones del negocio:
- Un mismo avion no puede tener dos vuelos el mismo dia a la misma hora.
vuelo_ides un codigo unico asignado por el sistema.
Ver respuesta
Clave candidata 1: {vuelo_id}
- Es unico por definicion (codigo asignado por el sistema).
- Es minimal: un solo atributo, no se puede reducir.
Clave candidata 2: {avion_id, fecha, hora_salida}
- Por la restriccion del negocio, un avion no puede estar en dos vuelos al mismo dia/hora, asi que esta combinacion es unica.
- Es minimal: quitar
avion_idfalla (V001 y V003 comparten fecha+hora), quitarfechafalla (V001 y V004 comparten avion+hora), quitarhora_salidafalla (V001 y V004 comparten avion+fecha… no, V004 es fecha distinta. Pero V001 y V005 comparten avion_id? No. Revisemos: A320-1 solo aparece con 08:00, pero en fechas distintas. Sin hora_salida, (A320-1, 2026-03-20) y (A320-1, 2026-03-21) son unicos en los datos, pero la restriccion de negocio dice que un avion no puede tener dos vuelos al mismo dia y hora — sin la hora, podria haber dos vuelos del mismo avion el mismo dia a horas distintas). Es minimal.
No son candidatas:
{aerolinea}sola: AV se repite.{origen, destino, fecha}: (BOG, MDE, 2026-03-20) se repite en V001 y V002.{vuelo_id, fecha}: es superclave pero no minimal ya quevuelo_idsolo ya es unico.
Pregunta 3 — Algebra relacional: seleccion, proyeccion y join (10 pts)
Dado el esquema:
Escribe las expresiones en algebra relacional para:
a) Nombres de empleados con salario mayor a 5000.
b) Nombres de empleados que trabajan en departamentos ubicados en ‘Medellin’.
Ver respuesta
a) Seleccion + proyeccion:
b) Join + seleccion + proyeccion:
Tambien es valido en pasos:
Pregunta 4 — Division en algebra relacional (10 pts)
Dado el esquema:
Donde OBLIGATORIOS contiene los cursos que todo estudiante debe tomar:
| curso_id |
|---|
| BD |
| Redes |
| SO |
Y el estado de INSCRIPCION es:
| est_id | curso_id |
|---|---|
| E1 | BD |
| E1 | Redes |
| E1 | SO |
| E1 | IA |
| E2 | BD |
| E2 | Redes |
| E3 | BD |
| E3 | SO |
| E3 | Redes |
| E3 | SO |
a) Escribe la expresion de algebra relacional que encuentra los estudiantes inscritos en todos los cursos obligatorios.
b) Traza el resultado paso a paso.
Ver respuesta
a) Division:
Que se expande como:
b) Traza paso a paso:
Paso 1 — :
| est_id |
|---|
| E1 |
| E2 |
| E3 |
Paso 2 — Paso 1 OBLIGATORIOS (3 estudiantes x 3 cursos = 9 combinaciones):
| est_id | curso_id |
|---|---|
| E1 | BD |
| E1 | Redes |
| E1 | SO |
| E2 | BD |
| E2 | Redes |
| E2 | SO |
| E3 | BD |
| E3 | Redes |
| E3 | SO |
Paso 3 — Paso 2 INSCRIPCION (combinaciones que faltan en INSCRIPCION):
| est_id | curso_id |
|---|---|
| E2 | SO |
(E2 no esta inscrito en SO)
Paso 4 — (Paso 3):
| est_id |
|---|
| E2 |
Paso 5 — Paso 1 Paso 4 = RESULTADO:
| est_id |
|---|
| E1 |
| E3 |
E1 y E3 estan inscritos en todos los cursos obligatorios. E2 no (le falta SO).
Pregunta 5 — SQL: GROUP BY + HAVING con Sakila (10 pts)
Usando la base de datos Sakila, escribe una consulta que muestre las categorias de peliculas que tienen un promedio de duracion (length) mayor a 120 minutos, mostrando el nombre de la categoria, la cantidad de peliculas y la duracion promedio. Ordena por duracion promedio descendente.
Ver respuesta
SELECT c.name AS categoria,
COUNT(*) AS num_peliculas,
ROUND(AVG(f.length), 1) AS duracion_promedio
FROM film f
JOIN film_category fc ON f.film_id = fc.film_id
JOIN category c ON fc.category_id = c.category_id
GROUP BY c.name
HAVING AVG(f.length) > 120
ORDER BY duracion_promedio DESC;Explicacion del orden de ejecucion:
- FROM/JOIN: combina film, film_category y category.
- GROUP BY c.name: agrupa por categoria.
- HAVING AVG(f.length) > 120: filtra solo categorias cuyo promedio de duracion supera 120 min.
- SELECT: proyecta nombre, conteo y promedio.
- ORDER BY: ordena por promedio descendente.
Nota: no se puede usar WHERE AVG(f.length) > 120 porque WHERE filtra filas individuales antes de agrupar y no puede contener funciones de agregacion.
Pregunta 6 — SQL: LEFT JOIN para encontrar “nunca rento” (10 pts)
Usando Sakila, escribe una consulta que encuentre todos los clientes que nunca han realizado un alquiler (rental). Muestra customer_id, first_name, last_name y email.
Usa la tecnica de LEFT JOIN + IS NULL.
Ver respuesta
SELECT c.customer_id,
c.first_name,
c.last_name,
c.email
FROM customer c
LEFT JOIN rental r ON c.customer_id = r.customer_id
WHERE r.rental_id IS NULL;Como funciona:
LEFT JOINretorna TODOS los clientes, incluso los que no tienen match en rental.- Para los clientes sin rentals, todas las columnas de
rentalson NULL. WHERE r.rental_id IS NULLfiltra exactamente esos: los que no tienen ningun rental.
Alternativa equivalente con NOT EXISTS:
SELECT c.customer_id, c.first_name, c.last_name, c.email
FROM customer c
WHERE NOT EXISTS (
SELECT 1 FROM rental r
WHERE r.customer_id = c.customer_id
);Ambas son correctas. NOT EXISTS suele ser mas eficiente en tablas grandes.
Pregunta 7 — Diagnostico de error SQL (10 pts)
Un estudiante escribio la siguiente consulta para encontrar las tiendas (store) que tienen mas de 50 clientes:
SELECT s.store_id, COUNT(c.customer_id) AS total_clientes
FROM store s
JOIN customer c ON s.store_id = c.store_id
WHERE COUNT(c.customer_id) > 50
GROUP BY s.store_id;
a) Explica por que esta consulta produce un error.
b) Escribe la version corregida.
Ver respuesta
a) La consulta falla porque usa una funcion de agregacion (COUNT) en la clausula WHERE. WHERE filtra filas individuales antes de que se formen los grupos (antes de GROUP BY), por lo tanto no tiene acceso a funciones de agregacion. Las funciones de agregacion solo se pueden usar en:
- HAVING (filtrar grupos)
- SELECT (proyectar valores agregados)
- ORDER BY (ordenar por agregados)
El orden de ejecucion es: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. WHERE se ejecuta antes de GROUP BY, asi que COUNT no tiene sentido ahi.
b) Version corregida — mover la condicion de agregacion a HAVING:
SELECT s.store_id, COUNT(c.customer_id) AS total_clientes
FROM store s
JOIN customer c ON s.store_id = c.store_id
GROUP BY s.store_id
HAVING COUNT(c.customer_id) > 50;Pregunta 8 — SQL: subconsulta correlacionada (10 pts)
Usando Sakila, escribe una consulta que muestre los actores que han actuado en mas peliculas que el promedio de peliculas por actor. Muestra first_name, last_name y la cantidad de peliculas.
Usa una subconsulta correlacionada (o una subconsulta escalar en HAVING).
Ver respuesta
Opcion 1 — subconsulta escalar en HAVING:
SELECT a.first_name, a.last_name, COUNT(*) AS num_peliculas
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
GROUP BY a.actor_id, a.first_name, a.last_name
HAVING COUNT(*) > (
SELECT AVG(cnt)
FROM (
SELECT COUNT(*) AS cnt
FROM film_actor
GROUP BY actor_id
) AS promedios
);Explicacion:
- La subconsulta mas interna calcula cuantas peliculas tiene cada actor.
- La subconsulta del medio calcula el promedio de esas cantidades.
- HAVING compara el conteo de cada actor contra ese promedio global.
Opcion 2 — subconsulta correlacionada en WHERE:
SELECT a.first_name, a.last_name,
(SELECT COUNT(*) FROM film_actor fa WHERE fa.actor_id = a.actor_id) AS num_peliculas
FROM actor a
WHERE (SELECT COUNT(*) FROM film_actor fa WHERE fa.actor_id = a.actor_id) > (
SELECT AVG(cnt) FROM (
SELECT COUNT(*) AS cnt FROM film_actor GROUP BY actor_id
) AS promedios
);Aqui la subconsulta SELECT COUNT(*) ... WHERE fa.actor_id = a.actor_id es correlacionada porque referencia a.actor_id de la consulta externa, y se re-ejecuta para cada actor.
Pregunta 9 — NOT IN con NULLs (10 pts)
Considera las siguientes tablas:
EMPLEADO (emp_id, nombre, dep_id)
| emp_id | nombre | dep_id |
|--------|--------|--------|
| 1 | Ana | 10 |
| 2 | Luis | 20 |
| 3 | Maria | 30 |
| 4 | Pedro | 10 |
DEPARTAMENTO (dep_id, nombre_dep)
| dep_id | nombre_dep |
|--------|------------|
| 10 | IT |
| 20 | RRHH |
| NULL | Sin asignar|
Un estudiante ejecuta:
SELECT nombre FROM EMPLEADO
WHERE dep_id NOT IN (SELECT dep_id FROM DEPARTAMENTO);
a) Que resultado devuelve esta consulta? Explica paso a paso usando logica de 3 valores.
b) Como la corriges para obtener los empleados cuyo dep_id no esta en ningun departamento valido?
Ver respuesta
a) La consulta devuelve un resultado vacio (0 filas). Explicacion:
La subconsulta SELECT dep_id FROM DEPARTAMENTO retorna: (10, 20, NULL).
Para cada empleado, NOT IN (10, 20, NULL) se expande a:
El problema esta en dep_id <> NULL, que siempre se evalua como UNKNOWN.
Veamos con Maria (dep_id = 30):
30 <> 10= TRUE30 <> 20= TRUE30 <> NULL= UNKNOWNTRUE AND TRUE AND UNKNOWN= UNKNOWN
WHERE solo pasa filas con TRUE. UNKNOWN no es TRUE, asi que Maria no aparece.
Esto pasa con todos los empleados: la presencia de NULL en la lista hace que NOT IN siempre evalue a UNKNOWN, y el resultado es siempre vacio.
b) Dos soluciones:
Solucion 1 — filtrar NULLs en la subconsulta:
SELECT nombre FROM EMPLEADO
WHERE dep_id NOT IN (
SELECT dep_id FROM DEPARTAMENTO WHERE dep_id IS NOT NULL
);Ahora la lista es (10, 20). Maria (dep_id=30): 30 <> 10 AND 30 <> 20 = TRUE. Resultado: Maria.
Solucion 2 — usar NOT EXISTS (inmune a NULLs):
SELECT nombre FROM EMPLEADO e
WHERE NOT EXISTS (
SELECT 1 FROM DEPARTAMENTO d WHERE d.dep_id = e.dep_id
);NOT EXISTS funciona porque NULL = 30 evalua a UNKNOWN, que NOT EXISTS trata como “no encontro fila”, que es el comportamiento correcto.
Pregunta 10 — Pattern matching: LIKE (10 pts)
Para cada descripcion, escribe la expresion SQL con LIKE que la implemente correctamente. Usa la tabla film de Sakila (columna title).
a) Peliculas cuyo titulo empieza con “THE”.
b) Peliculas cuyo titulo contiene la palabra “LOVE” en cualquier posicion.
c) Peliculas cuyo titulo tiene exactamente 5 caracteres.
Ver respuesta
a) Empieza con “THE”:
SELECT title FROM film
WHERE title LIKE 'THE%';% al final: cualquier secuencia de caracteres despues de “THE”.
b) Contiene “LOVE” en cualquier posicion:
SELECT title FROM film
WHERE title LIKE '%LOVE%';% al inicio y al final: “LOVE” puede estar en cualquier parte del titulo.
c) Exactamente 5 caracteres:
SELECT title FROM film
WHERE title LIKE '_____';Cada _ representa exactamente un caracter. 5 guiones bajos = exactamente 5 caracteres.
Alternativa para (c) sin LIKE:
SELECT title FROM film
WHERE LENGTH(title) = 5;Ambas son validas, pero en un parcial donde piden LIKE, la primera es la respuesta esperada.