GROUPING SETS, ROLLUP y CUBE para agregaciones jerárquicas

Avanzado
SQL
SQL
Actualizado: 18/04/2026

Diagrama: Sql grouping sets rollup cube

Problema que resuelven

Imagina que necesitas un informe de ventas con total por producto, total por categoría y total general en una sola salida. Sin extensiones estándar, habría que hacer tres consultas y unirlas con UNION ALL:

-- Solucion sin extensiones: 3 queries + UNION
SELECT categoria, producto, SUM(ventas) FROM pedidos GROUP BY categoria, producto
UNION ALL
SELECT categoria, NULL,        SUM(ventas) FROM pedidos GROUP BY categoria
UNION ALL
SELECT NULL,      NULL,        SUM(ventas) FROM pedidos;

Las extensiones GROUPING SETS, ROLLUP y CUBE (estándar SQL:1999, soportadas por PostgreSQL, SQL Server, Oracle, MySQL 8+) simplifican esto a una sola consulta, además de aprovechar una sola pasada sobre los datos.

GROUPING SETS: combinaciones explícitas

GROUPING SETS permite listar exactamente qué combinaciones de columnas quieres agregar:

SELECT categoria, producto, SUM(ventas) AS total
FROM pedidos
GROUP BY GROUPING SETS (
    (categoria, producto),   -- detalle por producto
    (categoria),             -- subtotal por categoria
    ()                       -- total general
)
ORDER BY categoria NULLS LAST, producto NULLS LAST;

Salida con subtotales:

 categoria  | producto | total
------------+----------+-------
 Electronica| Laptop   |  5000
 Electronica| Tablet   |  3000
 Electronica| NULL     |  8000   <- subtotal categoria
 Ropa       | Camiseta |  1500
 Ropa       | NULL     |  1500   <- subtotal categoria
 NULL       | NULL     |  9500   <- total general

ROLLUP: subtotales jerárquicos

ROLLUP(a, b, c) es azúcar sintáctico equivalente a:

GROUPING SETS ((a, b, c), (a, b), (a), ())

Es decir, produce los agregados quitando columnas de derecha a izquierda. Ideal para jerarquías naturales como año-mes-día o país-región-ciudad:

SELECT anio, mes, dia, SUM(ingresos) AS total
FROM operaciones
GROUP BY ROLLUP (anio, mes, dia)
ORDER BY anio, mes, dia;

Produce: detalle por día, subtotal por mes (dia=NULL), subtotal por año (mes=NULL, dia=NULL), total general (todos NULL).

ROLLUP múltiple

También puedes combinar varios ROLLUP:

-- Por region y por periodo independientemente
SELECT pais, region, anio, mes, SUM(ventas)
FROM pedidos
GROUP BY ROLLUP (pais, region), ROLLUP (anio, mes);

CUBE: todas las combinaciones

CUBE(a, b, c) genera todas las combinaciones posibles de presencia/ausencia de las columnas (2^n grupos). Para 3 columnas son 8 grupos:

SELECT categoria, region, canal, SUM(ventas) AS total
FROM pedidos
GROUP BY CUBE (categoria, region, canal);

Los grupos generados son:

  • (categoria, region, canal)
  • (categoria, region)
  • (categoria, canal)
  • (categoria)
  • (region, canal)
  • (region)
  • (canal)
  • () total general

CUBE es útil para dashboards OLAP donde el usuario puede cruzar cualquier combinación de dimensiones. Cuidado: el coste crece exponencialmente con el número de columnas; no abuses.

La función GROUPING: distinguir NULL de NULL

Un problema habitual: en el resultado aparece NULL tanto porque la columna se ha agregado (subtotal) como porque el dato original era NULL. Para distinguirlos, la función GROUPING(col) devuelve 1 si la columna está agrupada (agregada) y 0 si su valor es el real:

SELECT
    CASE WHEN GROUPING(categoria) = 1 THEN 'TOTAL' ELSE categoria END AS categoria,
    CASE WHEN GROUPING(producto) = 1 THEN 'SUBTOTAL' ELSE producto END AS producto,
    SUM(ventas) AS total
FROM pedidos
GROUP BY ROLLUP (categoria, producto);

Resultado legible sin ambigüedad de NULLs:

 categoria  | producto | total
------------+----------+-------
 Electronica| Laptop   |  5000
 Electronica| Tablet   |  3000
 Electronica| SUBTOTAL |  8000
 Ropa       | Camiseta |  1500
 Ropa       | SUBTOTAL |  1500
 TOTAL      | SUBTOTAL |  9500

También existe GROUPING_ID(c1, c2, ...) que devuelve un bitmap con los niveles activos de agregación. Útil para ordenar jerarquías complejas.

Caso real: reporte financiero mensual

Un reporte típico de P&L agrupado por centro de coste, subcategoría y cuenta, con subtotales a todos los niveles:

SELECT
    CASE WHEN GROUPING(centro) = 1   THEN 'TOTAL EMPRESA' ELSE centro END AS centro,
    CASE WHEN GROUPING(categoria) = 1 THEN 'TOTAL CENTRO' ELSE categoria END AS categoria,
    CASE WHEN GROUPING(cuenta) = 1    THEN 'TOTAL CATEGORIA' ELSE cuenta END AS cuenta,
    SUM(importe) AS importe
FROM movimientos_contables
WHERE anio = 2026 AND mes = 4
GROUP BY ROLLUP (centro, categoria, cuenta)
ORDER BY GROUPING(centro), centro, GROUPING(categoria), categoria, GROUPING(cuenta), cuenta;

El truco del ORDER BY GROUPING(...) asegura que los subtotales aparezcan debajo de los detalles en lugar de mezclados (las filas agregadas tienen GROUPING=1, las detalladas GROUPING=0, así se ordenan primero los detalles y luego los subtotales).

Rendimiento

Estas extensiones suelen ser más rápidas que un UNION ALL de varias queries porque el motor puede:

  1. Hacer una única pasada sobre los datos.
  2. Reutilizar estructuras hash o de sort entre los distintos niveles de agregación.

En PostgreSQL el planner elige entre GroupAggregate (cuando los datos ya están ordenados) y HashAggregate (cuando no). Verifica con EXPLAIN ANALYZE:

EXPLAIN (ANALYZE, BUFFERS)
SELECT categoria, producto, SUM(ventas)
FROM pedidos
GROUP BY ROLLUP (categoria, producto);

Limitaciones y diferencias entre motores

| Motor | GROUPING SETS | ROLLUP | CUBE | GROUPING() | |-------|---------------|--------|------|------------| | PostgreSQL 9.5+ | ✓ | ✓ | ✓ | ✓ | | SQL Server | ✓ | ✓ | ✓ | ✓ | | Oracle | ✓ | ✓ | ✓ | ✓ | | MySQL 8.0+ | ✗ | WITH ROLLUP (sintaxis vieja) | ✗ | ✓ |

En MySQL 8 se usa la sintaxis legacy GROUP BY col1, col2 WITH ROLLUP que solo equivale a ROLLUP, no soporta GROUPING SETS ni CUBE.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SQL es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de SQL

Explora más contenido relacionado con SQL y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Usar GROUPING SETS para combinar en una consulta varios niveles de agregacion. Aplicar ROLLUP para generar subtotales jerarquicos (ano-mes-dia, pais-region-ciudad). Aplicar CUBE para todas las combinaciones posibles. Usar la funcion GROUPING(col) para distinguir NULL reales de NULL de agregacion. Combinar ROLLUP con ORDER BY para informes ordenados correctamente.