Apunte Práctico — SQL
Unidad V · Bases de Datos · UTN · Parcial 25-jun-2026
SELECT — Estructura base
El orden de las cláusulas es fijo
SELECT col1, col2, ... -- qué columnas mostrar FROM tabla -- de qué tabla WHERE condición -- filtro de filas (antes de agrupar) GROUP BY col1 -- agrupar resultados HAVING condición_de_grupo -- filtro sobre grupos ORDER BY col1 ASC|DESC -- ordenar resultado LIMIT n; -- limitar cantidad de filas
💡
Orden de ejecución real: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
El SELECT se ejecuta después del WHERE y del GROUP BY — por eso no podés usar alias del SELECT en el WHERE.
El SELECT se ejecuta después del WHERE y del GROUP BY — por eso no podés usar alias del SELECT en el WHERE.
-- DISTINCT: elimina filas duplicadas SELECT DISTINCT color FROM Parts; -- Alias de columna y tabla SELECT p.sid AS proveedor_id, p.sname AS nombre FROM Proveedores p;
WHERE — Filtros y operadores
Filtra filas antes de cualquier agrupación
Comparación
=igual<>o!=distinto< > <= >=
Lógicos
AND— ambas condicionesOR— al menos unaNOT— negación
Rango y lista
BETWEEN a AND bIN (v1, v2, ...)NOT IN (...)
Texto y nulos
LIKE 'Bo%'— empieza con BoLIKE '%ing'— termina en ingIS NULL/IS NOT NULL
SELECT * FROM Proveedores WHERE address = '221 Packer Street' OR sid IN (1, 3, 5); SELECT * FROM Empleados WHERE salary BETWEEN 80000 AND 120000; SELECT * FROM Avion WHERE aname LIKE 'Boeing%'; -- cualquier avión Boeing
JOINs — Combinar tablas
El más usado en parciales: INNER JOIN
| Tipo | Qué devuelve |
|---|---|
INNER JOIN | Solo filas que coinciden en ambas tablas |
LEFT JOIN | Todas las de la izquierda + las que coinciden de la derecha (NULL si no hay) |
RIGHT JOIN | Todas las de la derecha + coincidencias de la izquierda |
CROSS JOIN | Producto cartesiano — todas las combinaciones |
-- INNER JOIN (el más común) SELECT p.sname, pt.pname, c.cost FROM Proveedores p JOIN Catalog c ON p.sid = c.sid JOIN Parts pt ON c.pid = pt.pid WHERE pt.color = 'roja'; -- Self JOIN: comparar filas de la misma tabla SELECT c1.sid AS sid1, c2.sid AS sid2 FROM Catalog c1 JOIN Catalog c2 ON c1.pid = c2.pid WHERE c1.cost > c2.cost; -- ej 9: proveedor que cobra más que otro
⚠️
Truco parcial:
JOIN solo = INNER JOIN. Siempre especificá el ON con la clave foránea correcta, no mezcles claves distintas.Funciones de Agregación
Operan sobre grupos de filas
| Función | Qué hace | Ignora NULL |
|---|---|---|
COUNT(*) | Cuenta todas las filas del grupo | No |
COUNT(col) | Cuenta filas donde col no es NULL | Sí |
SUM(col) | Suma los valores | Sí |
AVG(col) | Promedio | Sí |
MAX(col) | Valor máximo | Sí |
MIN(col) | Valor mínimo | Sí |
SELECT COUNT(*) AS total_empleados FROM Empleados; SELECT SUM(salary) AS masa_salarial FROM Empleados; -- ej j SELECT MAX(salary) FROM Empleados; -- mayor salario
GROUP BY / HAVING
Agrupar filas y filtrar grupos
🔑
Regla: toda columna en el SELECT que no sea una función de agregación, debe estar en el GROUP BY.
WHERE filtra filas antes de agrupar · HAVING filtra grupos después de agrupar.
WHERE filtra filas antes de agrupar · HAVING filtra grupos después de agrupar.
-- Cantidad de aviones por piloto (ej h, i) SELECT eid, COUNT(aid) AS cant_aviones FROM Certificados GROUP BY eid HAVING COUNT(aid) = 3; -- exactamente 3 aviones (ej i) -- Presupuesto total por gerente (ej d, e) SELECT managerid, SUM(budget) AS total FROM Dept GROUP BY managerid HAVING SUM(budget) > 5000000; -- ej d -- Partes con al menos 2 proveedores (ej 10) SELECT pid FROM Catalog GROUP BY pid HAVING COUNT(DISTINCT sid) >= 2; -- Promedio de edad por nivel (ej f) SELECT level, AVG(age) AS prom_edad FROM Student GROUP BY level HAVING level <> 'JR'; -- ej g: excepto JR
Subconsultas
Consultas dentro de consultas — el corazón del parcial
IN / NOT IN
-- eids de pilotos Boeing (ej a) SELECT eid FROM Certificados WHERE aid IN ( SELECT aid FROM Avion WHERE aname LIKE 'Boeing%' ); -- pilotos con rango > 3000 que NO son Boeing (ej e) SELECT ename FROM Empleados WHERE eid IN ( SELECT eid FROM Certificados JOIN Avion USING(aid) WHERE cruisingrange > 3000 ) AND eid NOT IN ( SELECT eid FROM Certificados JOIN Avion USING(aid) WHERE aname LIKE 'Boeing%' );
EXISTS / NOT EXISTS
Más eficiente que IN cuando la subconsulta es correlacionada. Devuelve verdadero si la subconsulta retorna al menos una fila.
-- Estudiantes no inscriptos en ninguna clase (ej j) SELECT sname FROM Student s WHERE NOT EXISTS ( SELECT 1 FROM Inscripto i WHERE i.snum = s.snum -- correlación: referencia a la fila externa );
Subconsulta escalar (devuelve un solo valor)
-- partes más caras de Yosemite Sham (ej 11) SELECT pid FROM Catalog c JOIN Proveedores p ON c.sid = p.sid WHERE p.sname = 'Yosemite Sham' AND c.cost = ( SELECT MAX(c2.cost) FROM Catalog c2 JOIN Proveedores p2 ON c2.sid = p2.sid WHERE p2.sname = 'Yosemite Sham' );
ALL / ANY
-- aviones que pueden cubrir distancia Bonn→Madras (ej c) SELECT aid FROM Avion WHERE cruisingrange >= ( SELECT distance FROM Vuelos WHERE from_city = 'Bonn' AND to_city = 'Madras' );
UNION · INTERSECT · EXCEPT
Operaciones de conjuntos — mismas columnas y tipos
| Operador | Equivale a AR | Elimina duplicados |
|---|---|---|
UNION | ∪ Unión | Sí |
UNION ALL | ∪ sin dedup | No |
INTERSECT | ∩ Intersección | Sí |
EXCEPT | − Diferencia | Sí |
-- sids que proveen parte roja O parte verde (ej 2) SELECT sid FROM Catalog JOIN Parts USING(pid) WHERE color='roja' UNION SELECT sid FROM Catalog JOIN Parts USING(pid) WHERE color='verde'; -- sids que proveen roja Y verde (ej 4) SELECT sid FROM Catalog JOIN Parts USING(pid) WHERE color='roja' INTERSECT SELECT sid FROM Catalog JOIN Parts USING(pid) WHERE color='verde';
⚠️
Las columnas de ambas partes deben ser compatibles en cantidad y tipo. Usá alias si los nombres difieren.
División en SQL — "TODOS"
No existe como operador — se implementa con NOT EXISTS doble
🔑
Cada vez que el enunciado diga "TODOS" o "CADA" — pensá en división.
En SQL se implementa con doble
En SQL se implementa con doble
NOT EXISTS: "no existe ningún X para el que no existe Y".Patrón: ¿Quién provee CADA parte? (ej 5)
-- Traducción lógica: "no existe ninguna parte que este proveedor NO provea" SELECT s.sid FROM Proveedores s WHERE NOT EXISTS ( -- no existe ninguna parte... SELECT 1 FROM Parts pt WHERE NOT EXISTS ( -- ...que NO esté en el catálogo de este proveedor SELECT 1 FROM Catalog c WHERE c.sid = s.sid AND c.pid = pt.pid ) );
Patrón: ¿Quién provee CADA parte roja? (ej 6)
SELECT s.sid FROM Proveedores s WHERE NOT EXISTS ( SELECT 1 FROM Parts pt WHERE pt.color = 'roja' -- solo las rojas AND NOT EXISTS ( SELECT 1 FROM Catalog c WHERE c.sid = s.sid AND c.pid = pt.pid ) );
Alternativa con COUNT (más legible)
-- Proveedores que tienen en catálogo TODAS las partes rojas SELECT sid FROM Catalog c JOIN Parts p USING(pid) WHERE p.color = 'roja' GROUP BY sid HAVING COUNT(DISTINCT pid) = ( SELECT COUNT(*) FROM Parts WHERE color = 'roja' );
Máximo y 2do mayor salario
Patrones que siempre caen en parcial
El mayor salario (ej f)
-- Opción 1: con MAX SELECT eid FROM Empleados WHERE salary = (SELECT MAX(salary) FROM Empleados); -- Opción 2: no existe nadie que gane más SELECT eid FROM Empleados e WHERE NOT EXISTS ( SELECT 1 FROM Empleados e2 WHERE e2.salary > e.salary );
El 2do mayor salario (ej g)
-- Opción 1: MAX excluyendo el máximo SELECT eid FROM Empleados WHERE salary = ( SELECT MAX(salary) FROM Empleados WHERE salary < (SELECT MAX(salary) FROM Empleados) ); -- Opción 2: existe exactamente 1 que gana más SELECT eid FROM Empleados e WHERE ( SELECT COUNT(DISTINCT salary) FROM Empleados WHERE salary > e.salary ) = 1;
El mayor COUNT (ej h — más aviones)
SELECT eid FROM Certificados GROUP BY eid HAVING COUNT(aid) = ( SELECT MAX(cnt) FROM ( SELECT COUNT(aid) AS cnt FROM Certificados GROUP BY eid ) AS sub );
Patrones frecuentes en parciales
"Empleados que trabajan en Hardware Y en Software" (ej 4a)
SELECT e.ename, e.age FROM Emp e WHERE e.eid IN ( SELECT w.eid FROM Works w JOIN Dept d USING(did) WHERE d.dname='Hardware' ) AND e.eid IN ( SELECT w.eid FROM Works w JOIN Dept d USING(did) WHERE d.dname='Software' );
"Solo departamentos con X" — gerentes que NO tienen ninguno fuera (ej 4b)
-- Gerentes que administran SOLO deptos con budget > 1M SELECT managerid FROM Dept GROUP BY managerid HAVING MIN(budget) > 1000000; -- si el mínimo > 1M, todos > 1M
"Estudiantes con mayor número de clases" (ej 3i)
SELECT s.sname FROM Student s JOIN Inscripto i USING(snum) GROUP BY s.snum, s.sname HAVING COUNT(*) = ( SELECT MAX(cnt) FROM ( SELECT COUNT(*) AS cnt FROM Inscripto GROUP BY snum ) sub );
"Dos clases a la misma hora" — self join (ej 3c)
SELECT DISTINCT s.sname FROM Student s JOIN Inscripto i1 ON s.snum = i1.snum JOIN Inscripto i2 ON s.snum = i2.snum AND i1.cname <> i2.cname JOIN Class c1 ON i1.cname = c1.name JOIN Class c2 ON i2.cname = c2.name WHERE c1.meets_at = c2.meets_at;
DML — Modificar datos
-- INSERT: agregar filas INSERT INTO Parts (pid, pname, color) VALUES (109, 'Tornillo', 'azul'); -- INSERT múltiple INSERT INTO Parts (pid, pname, color) VALUES (110, 'Perno', 'roja'), (111, 'Resorte', 'verde'); -- UPDATE: modificar filas existentes UPDATE Empleados SET salary = salary * 1.10 -- aumento del 10% WHERE eid = 101; -- DELETE: eliminar filas DELETE FROM Catalog WHERE cost > 500;
⚠️
UPDATE y DELETE sin WHERE afectan todas las filas. Siempre revisá el WHERE antes de ejecutar.
DDL — Definir estructura
-- CREATE TABLE con restricciones CREATE TABLE Jugador ( noJugador INT PRIMARY KEY, nombre VARCHAR(50) NOT NULL, edad INT CHECK (edad >= 16), equipoId INT REFERENCES Equipo(noEquipo) ); -- ALTER: agregar columna ALTER TABLE Jugador ADD posicion VARCHAR(30); -- ALTER: eliminar columna ALTER TABLE Jugador DROP COLUMN posicion; -- DROP: eliminar tabla completa (¡irreversible!) DROP TABLE Jugador; DROP TABLE IF EXISTS Jugador; -- sin error si no existe
Tipos de datos en PostgreSQL
| Tipo | Uso |
|---|---|
INTEGER / INT | Enteros |
REAL / FLOAT | Decimales de punto flotante |
NUMERIC(p,s) | Decimal exacto (p dígitos, s decimales) |
VARCHAR(n) | Texto variable hasta n caracteres |
TEXT | Texto sin límite |
BOOLEAN | TRUE / FALSE |
DATE | Fecha (YYYY-MM-DD) |
TIME | Hora (HH:MM:SS) |
TIMESTAMP | Fecha + hora |
BD · UTN · Martín Malgor · 2026