Funciones de agregación en SQL: COUNT, SUM, AVG y GROUP BY explicados

D
DanisCh
(Actualizado: ) • 17 min de lectura
Funciones de agregación en SQL: COUNT, SUM, AVG y GROUP BY explicados
Bases de Datos Conceptos de Base de Datos

Las funciones de agregación son las herramientas que convierten SQL de un lenguaje para buscar filas en uno para responder preguntas de negocio: ¿cuántos pedidos se hicieron este mes? ¿cuál es el producto más vendido? ¿qué cliente ha gastado más? Sin ellas, extraer información útil de una base de datos requeriría procesar todos los datos en la aplicación. Con ellas, el motor de base de datos hace el trabajo pesado directamente donde viven los datos.

En esta guía aprenderás todas las funciones de agregación principales, cómo usar GROUP BY para agrupar datos, cómo filtrar grupos con HAVING y cómo combinar todo esto para construir consultas analíticas reales.

El esquema de ejemplo

Todos los ejemplos usan estas tres tablas. Créalas para seguir los ejemplos en tu propia base de datos:

CREATE TABLE clientes (
  id       INT PRIMARY KEY AUTO_INCREMENT,
  nombre   VARCHAR(100) NOT NULL,
  ciudad   VARCHAR(50),
  pais     VARCHAR(50) DEFAULT 'España'
);

CREATE TABLE productos (
  id         INT PRIMARY KEY AUTO_INCREMENT,
  nombre     VARCHAR(100) NOT NULL,
  categoria  VARCHAR(50),
  precio     DECIMAL(10, 2) NOT NULL,
  stock      INT DEFAULT 0
);

CREATE TABLE pedidos (
  id          INT PRIMARY KEY AUTO_INCREMENT,
  cliente_id  INT REFERENCES clientes(id),
  producto_id INT REFERENCES productos(id),
  fecha       DATE NOT NULL,
  cantidad    INT NOT NULL,
  total       DECIMAL(10, 2) NOT NULL,
  estado      VARCHAR(20) DEFAULT 'completado'
);

-- Datos de ejemplo
INSERT INTO clientes VALUES
  (1, 'Ana García',    'Madrid',    'España'),
  (2, 'Carlos López',  'Barcelona', 'España'),
  (3, 'María Torres',  'Madrid',    'España'),
  (4, 'Luis Martín',   'Valencia',  'España'),
  (5, 'Sara Jiménez',  'Sevilla',   'España');

INSERT INTO productos VALUES
  (1, 'Laptop Pro',      'Electrónica', 1299.99, 15),
  (2, 'Teclado RGB',     'Accesorios',    89.99, 50),
  (3, 'Monitor 4K',      'Electrónica',  499.99, 20),
  (4, 'Ratón inalámbr.', 'Accesorios',    39.99, 80),
  (5, 'Auriculares BT',  'Audio',        149.99, 30),
  (6, 'Webcam HD',       'Accesorios',    69.99, 40);

INSERT INTO pedidos VALUES
  (1,  1, 1, '2026-01-10', 1, 1299.99, 'completado'),
  (2,  2, 2, '2026-01-15', 2,  179.98, 'completado'),
  (3,  1, 3, '2026-01-20', 1,  499.99, 'completado'),
  (4,  3, 4, '2026-02-05', 3,  119.97, 'completado'),
  (5,  2, 5, '2026-02-10', 1,  149.99, 'completado'),
  (6,  4, 2, '2026-02-14', 1,   89.99, 'completado'),
  (7,  1, 6, '2026-02-20', 2,  139.98, 'completado'),
  (8,  5, 1, '2026-03-01', 1, 1299.99, 'completado'),
  (9,  3, 5, '2026-03-05', 2,  299.98, 'completado'),
  (10, 2, 3, '2026-03-12', 1,  499.99, 'cancelado'),
  (11, 4, 4, '2026-03-18', 2,   79.98, 'completado'),
  (12, 5, 2, '2026-03-22', 1,   89.99, 'completado')

COUNT: contar filas

COUNT cuenta filas. Es la función de agregación más usada y tiene tres variantes con comportamientos distintos que conviene entender bien.

-- COUNT(*): cuenta TODAS las filas, incluidas las que tienen NULLs en cualquier columna
SELECT COUNT(*) AS total_pedidos
FROM pedidos;
-- 12

-- COUNT(columna): cuenta solo las filas donde esa columna NO es NULL
SELECT COUNT(cliente_id) AS pedidos_con_cliente
FROM pedidos;
-- 12 (en este caso igual porque no hay NULLs, pero la diferencia importa cuando los hay)

-- COUNT(DISTINCT columna): cuenta valores únicos no nulos
SELECT COUNT(DISTINCT cliente_id) AS clientes_que_han_pedido
FROM pedidos;
-- 5

-- Diferencia entre COUNT(*) y COUNT(columna) con NULLs
-- Si una fila tiene cliente_id = NULL:
-- COUNT(*)          → la cuenta
-- COUNT(cliente_id) → NO la cuenta
-- Casos de uso reales de COUNT

-- Total de pedidos por estado
SELECT estado, COUNT(*) AS cantidad
FROM pedidos
GROUP BY estado;
-- completado | 11
-- cancelado  |  1

-- Cuántos pedidos distintos ha hecho cada cliente
SELECT
  c.nombre,
  COUNT(p.id) AS num_pedidos
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre
ORDER BY num_pedidos DESC;

-- Productos pedidos más de una vez (contar apariciones en pedidos)
SELECT
  pr.nombre,
  COUNT(pe.id) AS veces_pedido
FROM productos pr
JOIN pedidos pe ON pe.producto_id = pr.id
GROUP BY pr.id, pr.nombre
HAVING COUNT(pe.id) > 1
ORDER BY veces_pedido DESC;

SUM: sumar valores

SUM suma los valores de una columna numérica. Ignora los valores NULL automáticamente. Si todas las filas son NULL, devuelve NULL, no cero.

-- Ingresos totales de todos los pedidos completados
SELECT SUM(total) AS ingresos_totales
FROM pedidos
WHERE estado = 'completado';
-- 4249.83

-- Total de unidades vendidas por producto
SELECT
  pr.nombre,
  SUM(pe.cantidad) AS unidades_vendidas,
  SUM(pe.total)    AS ingresos_generados
FROM productos pr
JOIN pedidos pe ON pe.producto_id = pr.id
WHERE pe.estado = 'completado'
GROUP BY pr.id, pr.nombre
ORDER BY ingresos_generados DESC;
-- Ingresos mensuales: agrupar por año y mes
SELECT
  YEAR(fecha)  AS anio,
  MONTH(fecha) AS mes,
  COUNT(*)     AS num_pedidos,
  SUM(total)   AS ingresos
FROM pedidos
WHERE estado = 'completado'
GROUP BY YEAR(fecha), MONTH(fecha)
ORDER BY anio, mes;

-- anio | mes | num_pedidos | ingresos
-- 2026 |   1 |     3       | 1979.96
-- 2026 |   2 |     4       |  579.92
-- 2026 |   3 |     4       | 1689.95
-- SUM condicional: ingresos y pérdidas en la misma fila
-- usando CASE WHEN dentro de SUM (técnica muy útil)
SELECT
  SUM(CASE WHEN estado = 'completado' THEN total ELSE 0 END) AS ingresos_reales,
  SUM(CASE WHEN estado = 'cancelado'  THEN total ELSE 0 END) AS pedidos_cancelados,
  COUNT(CASE WHEN estado = 'completado' THEN 1 END)          AS num_completados,
  COUNT(CASE WHEN estado = 'cancelado'  THEN 1 END)          AS num_cancelados
FROM pedidos;

AVG: calcular la media

AVG calcula el promedio aritmético de una columna numérica. Como SUM, ignora los NULL. Es equivalente a SUM(columna) / COUNT(columna), pero más conciso.

-- Importe medio por pedido
SELECT
  ROUND(AVG(total), 2) AS ticket_medio
FROM pedidos
WHERE estado = 'completado';
-- 386.35

-- Precio medio por categoría
SELECT
  categoria,
  ROUND(AVG(precio), 2)  AS precio_medio,
  MIN(precio)            AS precio_minimo,
  MAX(precio)            AS precio_maximo
FROM productos
GROUP BY categoria
ORDER BY precio_medio DESC;

-- categoria    | precio_medio | precio_minimo | precio_maximo
-- Electrónica  |   899.99     |    499.99     |   1299.99
-- Audio        |   149.99     |    149.99     |    149.99
-- Accesorios   |    66.66     |     39.99     |     89.99
-- Ticket medio por cliente: quién compra más caro de media
SELECT
  c.nombre,
  COUNT(p.id)           AS num_pedidos,
  ROUND(AVG(p.total), 2) AS ticket_medio,
  SUM(p.total)          AS gasto_total
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
WHERE p.estado = 'completado'
GROUP BY c.id, c.nombre
ORDER BY ticket_medio DESC;
-- Comparar el precio de cada producto con la media de su categoría
SELECT
  nombre,
  categoria,
  precio,
  ROUND(AVG(precio) OVER (PARTITION BY categoria), 2) AS media_categoria,
  ROUND(precio - AVG(precio) OVER (PARTITION BY categoria), 2) AS diferencia
FROM productos
ORDER BY categoria, diferencia DESC;

MIN y MAX: valor mínimo y máximo

MIN y MAX funcionan con números, fechas y cadenas de texto (orden alfabético). Son especialmente útiles para encontrar extremos dentro de grupos.

-- Fecha del primer y último pedido de cada cliente
SELECT
  c.nombre,
  MIN(p.fecha)  AS primer_pedido,
  MAX(p.fecha)  AS ultimo_pedido,
  DATEDIFF(MAX(p.fecha), MIN(p.fecha)) AS dias_como_cliente
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre
ORDER BY primer_pedido;
-- Producto más barato y más caro de cada categoría
SELECT
  categoria,
  MIN(precio) AS mas_barato,
  MAX(precio) AS mas_caro,
  MAX(precio) - MIN(precio) AS rango_precios
FROM productos
GROUP BY categoria;
-- Pedido de mayor importe de cada cliente (sin subconsultas)
-- Usando MAX con CASE para extraer el ID del pedido máximo
SELECT
  c.nombre,
  MAX(p.total)   AS pedido_maximo,
  MIN(p.total)   AS pedido_minimo,
  MAX(p.fecha)   AS ultimo_pedido
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
WHERE p.estado = 'completado'
GROUP BY c.id, c.nombre;

GROUP BY: agrupar filas

GROUP BY divide las filas en grupos y aplica las funciones de agregación a cada grupo por separado. Es la clave para pasar de "el total de todos los pedidos" a "el total de pedidos por cliente" o "por mes" o "por categoría".

La regla fundamental: toda columna que aparezca en el SELECT y no sea una función de agregación debe aparecer también en el GROUP BY.

-- Sin GROUP BY: un único resultado para toda la tabla
SELECT SUM(total) FROM pedidos;
-- 4749.82

-- Con GROUP BY: un resultado por cada valor único de estado
SELECT estado, SUM(total) AS total_por_estado
FROM pedidos
GROUP BY estado;

-- Con múltiples columnas en GROUP BY: un resultado por combinación única
SELECT
  YEAR(fecha)  AS anio,
  MONTH(fecha) AS mes,
  estado,
  COUNT(*)     AS pedidos,
  SUM(total)   AS total
FROM pedidos
GROUP BY YEAR(fecha), MONTH(fecha), estado
ORDER BY anio, mes, estado;
-- Agrupar por expresiones calculadas
-- Pedidos por trimestre
SELECT
  YEAR(fecha)                     AS anio,
  QUARTER(fecha)                  AS trimestre,
  COUNT(*)                        AS num_pedidos,
  SUM(total)                      AS ingresos,
  ROUND(AVG(total), 2)            AS ticket_medio
FROM pedidos
WHERE estado = 'completado'
GROUP BY YEAR(fecha), QUARTER(fecha)
ORDER BY anio, trimestre;
-- Agrupar por rangos de precio (bucketing)
SELECT
  CASE
    WHEN precio < 50              THEN 'Menos de 50€'
    WHEN precio BETWEEN 50 AND 99  THEN '50€ - 99€'
    WHEN precio BETWEEN 100 AND 499 THEN '100€ - 499€'
    ELSE '500€ o más'
  END AS rango_precio,
  COUNT(*)         AS num_productos,
  ROUND(AVG(precio), 2) AS precio_medio
FROM productos
GROUP BY
  CASE
    WHEN precio < 50              THEN 'Menos de 50€'
    WHEN precio BETWEEN 50 AND 99  THEN '50€ - 99€'
    WHEN precio BETWEEN 100 AND 499 THEN '100€ - 499€'
    ELSE '500€ o más'
  END
ORDER BY MIN(precio);

HAVING: filtrar grupos

HAVING filtra grupos después de que GROUP BY los haya formado, igual que WHERE filtra filas individuales antes de agrupar. Esta diferencia es crucial: no puedes usar funciones de agregación en el WHERE, pero sí en el HAVING.

-- ERROR: no se puede usar SUM en WHERE
SELECT cliente_id, SUM(total)
FROM pedidos
WHERE SUM(total) > 500    -- ← esto falla
GROUP BY cliente_id;

-- CORRECTO: usar HAVING para filtrar sobre el resultado de la agregación
SELECT cliente_id, SUM(total) AS gasto_total
FROM pedidos
WHERE estado = 'completado'  -- WHERE filtra filas ANTES de agrupar
GROUP BY cliente_id
HAVING SUM(total) > 500;     -- HAVING filtra grupos DESPUÉS de agrupar
-- Clientes con más de un pedido y gasto total superior a 300€
SELECT
  c.nombre,
  COUNT(p.id)  AS num_pedidos,
  SUM(p.total) AS gasto_total
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
WHERE p.estado = 'completado'
GROUP BY c.id, c.nombre
HAVING COUNT(p.id) > 1
  AND SUM(p.total) > 300
ORDER BY gasto_total DESC;
-- Categorías con precio medio superior a 100€ y más de un producto
SELECT
  categoria,
  COUNT(*)              AS num_productos,
  ROUND(AVG(precio), 2) AS precio_medio,
  SUM(stock)            AS stock_total
FROM productos
GROUP BY categoria
HAVING AVG(precio) > 100
  AND COUNT(*) > 1;
-- Meses con más de 2 pedidos completados
SELECT
  YEAR(fecha)  AS anio,
  MONTH(fecha) AS mes,
  COUNT(*)     AS pedidos,
  SUM(total)   AS total_mes
FROM pedidos
WHERE estado = 'completado'
GROUP BY YEAR(fecha), MONTH(fecha)
HAVING COUNT(*) > 2
ORDER BY anio, mes;

El orden correcto de las cláusulas SQL

SQL tiene un orden de escritura fijo y un orden de ejecución diferente. Confundirlos es la fuente de muchos errores al principio.

Orden de escrituraCláusulaOrden de ejecuciónQué hace
1SELECT6Define qué columnas devolver
2FROM1De qué tabla o tablas leer
3JOIN2Combinar tablas
4WHERE3Filtrar filas individuales
5GROUP BY4Agrupar filas en grupos
6HAVING5Filtrar grupos
7ORDER BY7Ordenar el resultado final
8LIMIT8Limitar el número de filas devueltas

Por eso no puedes usar un alias del SELECT en el WHERE o en el GROUP BY: el SELECT se evalúa después. Sí puedes usarlo en el ORDER BY porque este se evalúa al final.

-- ERROR: el alias 'gasto_total' no existe todavía cuando se evalúa HAVING en algunos motores
SELECT cliente_id, SUM(total) AS gasto_total
FROM pedidos
GROUP BY cliente_id
HAVING gasto_total > 500;   -- funciona en MySQL pero no en SQL estándar

-- CORRECTO y portable: repetir la expresión
SELECT cliente_id, SUM(total) AS gasto_total
FROM pedidos
GROUP BY cliente_id
HAVING SUM(total) > 500;

-- Sí funciona en ORDER BY (se evalúa al final)
SELECT cliente_id, SUM(total) AS gasto_total
FROM pedidos
GROUP BY cliente_id
ORDER BY gasto_total DESC;   -- ← aquí el alias sí está disponible

ROLLUP y CUBE: totales y subtotales automáticos

GROUP BY ROLLUP genera automáticamente subtotales y el total general además de los grupos normales. Es muy útil para informes.

-- Ingresos por ciudad y mes, con subtotales por ciudad y total general
SELECT
  COALESCE(c.ciudad, 'TOTAL GENERAL')  AS ciudad,
  COALESCE(CAST(MONTH(p.fecha) AS CHAR), 'Subtotal') AS mes,
  COUNT(*)     AS pedidos,
  SUM(p.total) AS ingresos
FROM pedidos p
JOIN clientes c ON c.id = p.cliente_id
WHERE p.estado = 'completado'
GROUP BY c.ciudad, MONTH(p.fecha) WITH ROLLUP
ORDER BY ciudad, mes;

-- Resultado incluye:
-- Madrid  | 1 | ... | ...     ← datos reales
-- Madrid  | 2 | ... | ...
-- Madrid  | NULL | ... | ...  ← subtotal de Madrid
-- TOTAL GENERAL | NULL | ... | ... ← total global

Funciones de ventana: agregación sin perder filas

Las funciones de ventana (window functions) aplican agregaciones sin colapsar las filas en grupos. Con GROUP BY obtienes una fila por grupo; con las funciones de ventana obtienes todas las filas con el resultado de la agregación añadido como columna extra.

-- Comparar el total de cada pedido con el total global y el total del cliente
SELECT
  c.nombre,
  p.fecha,
  p.total,
  SUM(p.total) OVER ()                          AS total_global,
  SUM(p.total) OVER (PARTITION BY c.id)         AS total_cliente,
  ROUND(p.total / SUM(p.total) OVER () * 100, 1) AS pct_del_total
FROM pedidos p
JOIN clientes c ON c.id = p.cliente_id
WHERE p.estado = 'completado'
ORDER BY c.nombre, p.fecha;
-- Ranking de clientes por gasto dentro de cada ciudad
SELECT
  c.nombre,
  c.ciudad,
  SUM(p.total) AS gasto_total,
  RANK() OVER (
    PARTITION BY c.ciudad
    ORDER BY SUM(p.total) DESC
  ) AS ranking_en_ciudad
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
WHERE p.estado = 'completado'
GROUP BY c.id, c.nombre, c.ciudad
ORDER BY c.ciudad, ranking_en_ciudad;
-- Total acumulado de ingresos por fecha (running total)
SELECT
  fecha,
  SUM(total)                                   AS ingresos_dia,
  SUM(SUM(total)) OVER (ORDER BY fecha)        AS ingresos_acumulados
FROM pedidos
WHERE estado = 'completado'
GROUP BY fecha
ORDER BY fecha;

Consultas analíticas completas

Aquí tienes tres consultas que integran todo lo aprendido y resuelven preguntas de negocio reales.

Informe completo de clientes

SELECT
  c.nombre,
  c.ciudad,
  COUNT(p.id)                               AS total_pedidos,
  SUM(p.total)                              AS gasto_total,
  ROUND(AVG(p.total), 2)                    AS ticket_medio,
  MIN(p.fecha)                              AS primer_pedido,
  MAX(p.fecha)                              AS ultimo_pedido,
  DATEDIFF(MAX(p.fecha), MIN(p.fecha))      AS dias_activo,
  COUNT(CASE WHEN p.estado = 'cancelado' THEN 1 END) AS pedidos_cancelados
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre, c.ciudad
ORDER BY gasto_total DESC;

Análisis de productos: ventas, ingresos y rotación de stock

SELECT
  pr.nombre,
  pr.categoria,
  pr.precio,
  pr.stock,
  COALESCE(COUNT(pe.id), 0)             AS veces_pedido,
  COALESCE(SUM(pe.cantidad), 0)         AS unidades_vendidas,
  COALESCE(SUM(pe.total), 0)            AS ingresos_generados,
  ROUND(
    COALESCE(SUM(pe.cantidad), 0) /
    NULLIF(pr.stock + COALESCE(SUM(pe.cantidad), 0), 0) * 100, 1
  )                                     AS tasa_rotacion_pct
FROM productos pr
LEFT JOIN pedidos pe ON pe.producto_id = pr.id
  AND pe.estado = 'completado'
GROUP BY pr.id, pr.nombre, pr.categoria, pr.precio, pr.stock
ORDER BY ingresos_generados DESC;

Dashboard mensual con variación respecto al mes anterior

WITH metricas_mensuales AS (
  SELECT
    YEAR(fecha)         AS anio,
    MONTH(fecha)        AS mes,
    COUNT(*)            AS pedidos,
    SUM(total)          AS ingresos,
    ROUND(AVG(total), 2) AS ticket_medio,
    COUNT(DISTINCT cliente_id) AS clientes_activos
  FROM pedidos
  WHERE estado = 'completado'
  GROUP BY YEAR(fecha), MONTH(fecha)
)
SELECT
  anio,
  mes,
  pedidos,
  ingresos,
  ticket_medio,
  clientes_activos,
  LAG(ingresos) OVER (ORDER BY anio, mes) AS ingresos_mes_anterior,
  ROUND(
    (ingresos - LAG(ingresos) OVER (ORDER BY anio, mes))
    / NULLIF(LAG(ingresos) OVER (ORDER BY anio, mes), 0) * 100, 1
  ) AS variacion_pct
FROM metricas_mensuales
ORDER BY anio, mes;

Errores frecuentes y cómo evitarlos

Columna en SELECT que no está en GROUP BY. Es el error más común al empezar. Si seleccionas una columna que no está en GROUP BY y no es una función de agregación, el resultado es ambiguo (¿qué valor de esa columna debería mostrar el grupo?). MySQL lo permite por defecto pero da resultados impredecibles. PostgreSQL y SQL estándar lo rechazan con un error.

-- ❌ Mal: nombre no está en GROUP BY ni es una agregación
SELECT nombre, ciudad, COUNT(*) AS pedidos
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
GROUP BY ciudad;   -- ¿qué nombre mostrar si hay varios por ciudad?

-- ✅ Bien: incluir todas las columnas no agregadas en GROUP BY
SELECT c.nombre, c.ciudad, COUNT(p.id) AS pedidos
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre, c.ciudad;

Usar WHERE en lugar de HAVING para condiciones sobre agregaciones.

-- ❌ Mal: WHERE no puede usar SUM()
SELECT cliente_id, SUM(total)
FROM pedidos
WHERE SUM(total) > 500
GROUP BY cliente_id;

-- ✅ Bien
SELECT cliente_id, SUM(total) AS gasto
FROM pedidos
GROUP BY cliente_id
HAVING SUM(total) > 500;

Olvidar que AVG ignora los NULL. Si quieres incluir los ceros en el promedio, debes convertir los NULL en cero con COALESCE.

-- Si algunos clientes no tienen pedidos (NULL tras el LEFT JOIN):
-- AVG calcula solo sobre los que SÍ tienen pedidos
SELECT AVG(p.total) FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id;

-- Para incluir los clientes sin pedidos como 0 en la media:
SELECT AVG(COALESCE(p.total, 0))
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id;

COUNT(*) con LEFT JOIN cuenta filas aunque no haya match. Si usas LEFT JOIN y quieres contar solo las filas que sí tienen coincidencia, usa COUNT(columna_de_la_tabla_derecha) en lugar de COUNT(*).

-- ❌ Cuenta 1 para clientes sin pedidos (la fila existe, con NULL en columnas de pedidos)
SELECT c.nombre, COUNT(*) AS pedidos
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre;

-- ✅ COUNT(p.id) devuelve 0 para clientes sin pedidos (porque p.id es NULL)
SELECT c.nombre, COUNT(p.id) AS pedidos
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre;

Resumen

  • COUNT(*) cuenta todas las filas. COUNT(columna) excluye NULLs. COUNT(DISTINCT columna) cuenta valores únicos.
  • SUM suma valores numéricos ignorando NULLs. Usa CASE WHEN dentro de SUM para sumas condicionales.
  • AVG calcula la media ignorando NULLs. Usa COALESCE si quieres incluir los ceros en el promedio.
  • MIN y MAX funcionan con números, fechas y texto. Útiles para encontrar extremos dentro de grupos.
  • GROUP BY agrupa filas. Toda columna no agregada del SELECT debe estar en el GROUP BY.
  • WHERE filtra filas antes de agrupar. HAVING filtra grupos después de agrupar. Nunca uses funciones de agregación en el WHERE.
  • Las funciones de ventana (OVER) aplican agregaciones sin colapsar las filas, permitiendo totales acumulados, rankings y comparativas en la misma consulta.
  • Las CTEs (WITH) hacen las consultas analíticas complejas mucho más legibles y mantenibles.

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a