Agregación y Vistas en Vivo: Resúmenes Reales
Otra vez PostgreSQL de verdad, compilado a WebAssembly, corriendo enteramente en tu navegador. Esta vez vas a convertir 14 ventas sueltas en resúmenes por categoría y por vendedor, filtrar esos resúmenes ya calculados, y guardarlos bajo un nombre con vistas — viendo en cada paso exactamente qué calcula el motor.
Bienvenido
Este taller es la práctica de laboratorio de agregación (GROUP BY/HAVING) y vistas en PostgreSQL. No vuelve a explicar la teoría desde cero —asume que ya viste la guía— y en cambio te da un PostgreSQL real para escribir cada consulta, viendo exactamente qué filas agrupa, cuáles filtra y qué guarda cada vista.
Si todavía no viste la teoría, repasa primero Agregación y Vistas antes de continuar. Este taller usa una sola tabla —ventas, con 14 filas— pensada para que cada resumen se vea clarísimo, sin el ruido de un catálogo completo.
Esta página corre su propia base de datos, independiente de cualquier otro taller que hayas abierto antes. Existe solo mientras esta pestaña siga abierta: si recargas la página, la cierras, o navegas a otra guía, se pierde todo y hay que empezar de nuevo desde "Prepara tu base de datos". Si te equivocás y querés reiniciar sin recargar, usa el botón "Reiniciar base de datos" de abajo.
✍️ Lo escribís vos
Cada bloque parte vacío. El enunciado te dice qué tenés que lograr — el código SQL lo escribís de cero.
🩻 Errores reales
Los mensajes de error que vas a ver son exactamente los que da PostgreSQL en producción.
🔢 14 ventas, 4 vendedores, 4 categorías
Suficientes filas para que agrupar por categoría o por vendedor dé resultados distintos entre sí — y para que HAVING tenga algo real que filtrar.
Zona de pruebas libres
Este bloque queda disponible durante toda la guía. Usalo en cualquier momento para probar una idea propia, revisar el estado de una tabla con SELECT * FROM tabla;, o experimentar con algo que se te ocurra a mitad de un ejercicio — sin desordenar los bloques guiados.
Prepara tu base de datos
Este bloque no es un ejercicio — ya viene resuelto. Crea una tabla, ventas, con 14 filas de ejemplo. Ejecútalo tal cual, una sola vez, antes de seguir.
Crea ventas con columnas vendedor, categoria y monto: 4 vendedores (Marisol, Iván, Daniela, Esteban) repartidos en 4 categorías (Electrónica, Ropa, Hogar, Deportes), con montos distintos entre sí para que cada resumen agrupado dé un número distinto y fácil de verificar a ojo.
El resultado final debería mostrarte 14 filas. Si contás menos o más, usa "🔄 Reiniciar base de datos" y vuelve a ejecutar este bloque — el resto del taller asume exactamente estos datos.
1. GROUP BY
Un grupo por cada valor distinto de la columna elegida, con una función de agregación calculada dentro de cada grupo.
Escribe un SELECT que agrupe ventas por categoria, trayendo categoria, COUNT(*) AS ventas y SUM(monto) AS total.
Deberían salir 4 filas, una por cada categoría distinta — sin importar que la tabla completa tenga 14 filas.
Ahora agrupa por vendedor en vez de categoria, trayendo vendedor, COUNT(*) AS ventas, SUM(monto) AS total y AVG(monto) AS promedio.
1.3 — Antes de ejecutar, predice: ¿cuántas filas devuelve SELECT COUNT(*) FROM ventas (sin GROUP BY)? ¿Y cuántas devuelve SELECT categoria, COUNT(*) FROM ventas GROUP BY categoria? Escribe las dos consultas en este mismo editor, una debajo de otra, para comprobarlo.
2. HAVING
Filtrar los grupos ya calculados, después de agrupar — no las filas individuales de la tabla.
Repite el ejercicio 1.1 (GROUP BY categoria con COUNT y SUM) y agrégale HAVING SUM(monto) > 700 para quedarte solo con las categorías que vendieron más de Bs 700 en total.
Deberían quedar Electrónica y Deportes. Ropa (Bs 300) y Hogar (Bs 645) no superan los Bs 700 del filtro, así que HAVING las descarta.
Agrupa por vendedor y agrega HAVING COUNT(*) >= 4 para quedarte solo con quienes hicieron 4 o más ventas.
Combina los tres filtros en una sola consulta: primero WHERE monto > 100 (descarta ventas chicas antes de agrupar), después GROUP BY vendedor, y por último HAVING COUNT(*) >= 3 (vendedores con al menos 3 ventas grandes).
Deberían quedar Marisol e Iván. Daniela y Esteban pierden una de sus ventas grandes al aplicar el WHERE (les queda una venta de Bs 100 o menos fuera del conteo), y terminan con menos de 3 ventas grandes cada uno.
3. ORDER BY y LIMIT sobre grupos
El resultado ya agregado se puede ordenar y recortar exactamente igual que filas comunes.
Sobre GROUP BY categoria con SUM(monto) AS total, ordena de mayor a menor y quedate con las 2 categorías que más vendieron usando LIMIT 2.
Sobre GROUP BY vendedor con SUM(monto) AS total, encuentra al vendedor con menos ventas totales usando ORDER BY total ASC y LIMIT 1.
Debería salir Daniela, con Bs 880 — el total más bajo de los cuatro vendedores.
4. Vistas
Guardar una consulta con nombre, para no reescribirla cada vez que alguien la necesita.
Crea una vista llamada resumen_vendedores a partir de la consulta del ejercicio 1.2: vendedor, COUNT(*) AS ventas, SUM(monto) AS total y AVG(monto) AS promedio, agrupando por vendedor.
Consulta la vista completa con SELECT * FROM resumen_vendedores;. Confirma que el resultado es idéntico al del ejercicio 1.2.
Sobre la vista, agrega WHERE total > 900 para quedarte solo con los vendedores que más vendieron. Ojo: acá es WHERE, no HAVING — para la vista, total ya es una columna común.
Deberían quedar Marisol (Bs 1435), Iván (Bs 1420) y Esteban (Bs 925). Daniela (Bs 880) no supera los Bs 900 del filtro.
Actualiza la vista con CREATE OR REPLACE VIEW resumen_vendedores, agregando MAX(monto) AS venta_max a las columnas que ya tenía. Después vuelve a consultarla completa con SELECT * para confirmar que la columna nueva aparece.
La vista no se borró ni se recreó desde cero: CREATE OR REPLACE VIEW actualizó su definición en el lugar. La consulta del ejercicio 4.3 seguiría funcionando igual si la volvieras a ejecutar, ahora con una columna más disponible.
Reto final: de la tabla a la vista
Sin reiniciar la base de datos, resuelve estos tres desafíos combinando GROUP BY, HAVING y vistas.
- 1. Con
GROUP BY categoria, traecategoria,COUNT(*) AS ventasySUM(monto) AS totalpara las 4 categorías, ordenado portotalde mayor a menor. - 2. Con
GROUP BY vendedoryHAVING, encuentra a los vendedores cuyo total de ventas supere Bs 900. - 3. Crea una vista llamada
top_categoriasque guarde el resultado del punto 1, pero recortado a las 2 categorías de mayor total (conORDER BYyLIMITdentro de la propia vista), y consúltala al final conSELECT *.
Repaso
Seis preguntas cortas sobre lo que acabas de observar en vivo. Tu progreso se guarda automáticamente en este navegador.
En el ejercicio 1.1 agrupaste las ventas por categoría. ¿Cuántas filas devolvió esa consulta, y por qué?
En el ejercicio 2.1 agregaste HAVING SUM(monto) > 700 sobre el GROUP BY por categoría. ¿Qué categorías quedaron afuera, y por qué?
Clasifica cada fragmento de consulta según qué hace.
Sobre el ejercicio 2.3 (WHERE monto > 100, GROUP BY vendedor, HAVING COUNT(*) >= 3).
1. El primer filtro en aplicarse, antes de que exista ningún grupo, es .
2. El filtro que se aplica al final, sobre los grupos ya armados, es .
En el ejercicio 4.4 actualizaste la vista con CREATE OR REPLACE VIEW, agregando MAX(monto). ¿Qué pasó con la consulta del ejercicio 4.3, que ya usaba resumen_vendedores antes de ese cambio?
Une cada cláusula con lo que hace en una consulta con agregación.
Cheat Sheet del taller
| Forma | Para qué sirve |
|---|---|
GROUP BY columna | Agrupa filas que comparten el mismo valor en esa columna. |
COUNT() / SUM() / AVG() / MAX() / MIN() | Funciones de agregación: un valor por grupo (o por toda la tabla, sin GROUP BY). |
WHERE | Filtra filas individuales, antes de agrupar. |
HAVING | Filtra grupos ya calculados, después de agrupar. |
... GROUP BY col ORDER BY total DESC LIMIT n | Ordena y recorta el resultado ya agregado. |
CREATE VIEW nombre AS SELECT ... | Guarda una consulta con nombre; no copia datos. |
SELECT * FROM vista | Se consulta como una tabla común. |
CREATE OR REPLACE VIEW ... | Reemplaza la definición de una vista sin borrarla. |
¿Sabías que...?
🧮 GROUP BY sin ORDER BY no promete ningún orden
El orden en que aparecieron las categorías en este taller es el que decidió PostgreSQL internamente, no uno garantizado por el estándar SQL. Si el orden importa para tu resultado, siempre hay que agregar un ORDER BY explícito.
🔒 Las vistas también sirven para seguridad
En vez de dar acceso directo a ventas completa, se le podría dar a alguien acceso solo a resumen_vendedores — ve los totales, no cada venta individual. Un adelanto de lo que vas a ver en la guía de roles y GRANT/REVOKE.
💾 Existen las vistas materializadas
A diferencia de las vistas de este taller, una vista materializada (MATERIALIZED VIEW) sí guarda una copia física del resultado, que hay que refrescar a mano — útil cuando la consulta original es cara de recalcular cada vez.