El síntoma: quieres resumir un resumen y el motor rechaza la consulta
Imagina que primero quieres sumar cuánto ha gastado cada cliente y, después, calcular el promedio de esos totales por cliente. Es tentador escribir las dos operaciones una dentro de la otra:
SELECT AVG(SUM(total)) AS promedio_total_por_cliente
FROM pedidos
GROUP BY cliente_id;En la base educativa local, SQLite rechaza esta consulta con misuse of aggregate function SUM(). Otros motores muestran mensajes diferentes, pero el problema conceptual es el mismo: SUM(total) ya resume varias filas de cada cliente y AVG(...) pretende resumir esos resultados sin crear antes una nueva relación intermedia.
Por qué no basta con colocar un agregado dentro de otro
Una función de agregación trabaja sobre el conjunto de filas visible en un nivel de consulta. Con GROUP BY cliente_id, ese nivel produce una fila por cliente después de calcular SUM(total). Para promediar esas filas resumidas necesitas que se conviertan en la entrada de otro nivel de consulta.
Modelo mental: primer nivel = «una fila por cliente con su total»; segundo nivel = «un único promedio calculado sobre esos totales». Dos preguntas de resumen distintas suelen necesitar dos etapas explícitas.
Los paréntesis solo cambian la forma de una expresión; no crean por sí solos una nueva etapa de filas. Por eso AVG(SUM(total)) no equivale a «primero SUM y luego AVG» de manera automática.
Corrección portable: crea primero el resumen interior
La forma más directa es usar una subconsulta que produzca una fila por cliente. La consulta exterior puede entonces promediar esa nueva columna:
SELECT ROUND(AVG(total_cliente), 2) AS promedio_total_por_cliente
FROM (
SELECT cliente_id,
SUM(total) AS total_cliente
FROM pedidos
GROUP BY cliente_id
) AS por_cliente;| promedio_total_por_cliente |
|---|
| 393.19 |
La subconsulta resuelve la primera granularidad: una fila por cliente_id. Después, AVG(total_cliente) recibe diez filas ya resumidas y calcula un único resultado.
La misma separación con una CTE
Si quieres hacer más visible la secuencia de cálculo, una CTE expresa la misma idea con un nombre intermedio:
WITH totales_por_cliente AS (
SELECT cliente_id,
SUM(total) AS total_cliente
FROM pedidos
GROUP BY cliente_id
)
SELECT ROUND(AVG(total_cliente), 2) AS promedio_total_por_cliente
FROM totales_por_cliente;La CTE no cambia la matemática: solo hace explícita la primera etapa. Es especialmente útil si después necesitas reutilizar el total por cliente en varias expresiones de la consulta exterior.
Antes de añadir una subconsulta, comprueba si el segundo agregado es realmente necesario
No toda agregación anidada expresa una pregunta distinta. Por ejemplo, esta consulta intenta sumar el número de pedidos de cada cliente:
SELECT SUM(COUNT(*))
FROM pedidos
GROUP BY cliente_id;Si tu única intención es obtener el total de pedidos, no necesitas contar por cliente y volver a sumar. La pregunta se reduce a:
SELECT COUNT(*) AS total_pedidos
FROM pedidos;| total_pedidos |
|---|
| 18 |
Separar etapas es correcto cuando existen dos granularidades reales; simplificar es mejor cuando el nivel intermedio no aporta información necesaria.
Cómo se manifiesta en distintos motores
| Motor | Comportamiento relevante | Idea práctica |
|---|---|---|
| PostgreSQL 18 | La documentación define el argumento de una expresión agregada como una expresión que no puede contener otra expresión agregada ni una función de ventana. | Calcula el agregado interior en una subconsulta o CTE y agrega fuera. |
| MySQL 8.4 | El catálogo oficial incluye el error 1111, ER_INVALID_GROUP_FUNC_USE («Invalid use of group function»), que aparece en usos inválidos de funciones de grupo. | No ocultes el problema cambiando modos: revisa el nivel de consulta. |
| SQL Server | La documentación de las funciones agregadas indica que sus expresiones no admiten otras funciones agregadas ni subconsultas en el argumento. | Materializa primero el resumen interior mediante una consulta derivada o CTE. |
| SQLite | La reproducción local de AVG(SUM(total)) falla con misuse of aggregate function SUM(). | Usa igualmente dos niveles; así mantienes una estructura portable y explícita. |
El texto exacto del error depende del motor y de la expresión usada. No memorices el mensaje: reconoce la forma agregado exterior → agregado interior en el mismo nivel.
Método de diagnóstico
- Localiza las funciones de agregación:
COUNT,SUM,AVG,MIN,MAXu otras del motor. - Comprueba si una aparece dentro del argumento de otra en el mismo
SELECT. - Escribe con palabras qué devuelve el agregado interior: por ejemplo, «total por cliente».
- Pregunta qué quieres calcular después sobre esas filas resumidas: por ejemplo, «promedio de los totales».
- Convierte la primera respuesta en una subconsulta o CTE y aplica el segundo agregado en la consulta exterior.
- Si el nivel intermedio no cambia la pregunta, simplifica la consulta en lugar de mantener dos agregaciones.
Práctica correctiva
Calcula el promedio del gasto total por cliente
Con la tabla pedidos, calcula primero el gasto total de cada cliente y después devuelve un único valor: el promedio de esos totales.
Criterio de éxito: no anidas AVG(SUM(...)) en el mismo nivel, la primera etapa produce una fila por cliente y el resultado final redondeado a dos decimales es 393.19.
cliente_id.SUM(total), por ejemplo total_cliente.AVG sobre total_cliente.Solución razonada
SELECT ROUND(AVG(total_cliente), 2) AS promedio_total_por_cliente
FROM (
SELECT cliente_id,
SUM(total) AS total_cliente
FROM pedidos
GROUP BY cliente_id
) AS por_cliente;La subconsulta devuelve diez filas, una por cliente con pedidos. La consulta exterior deja de trabajar con pedidos individuales y recibe esos diez totales como entrada. Sobre ellos calcula el promedio 393.19.
Qué debes recordar
- Una agregación anidada en el mismo nivel no crea automáticamente una segunda etapa de resumen.
- Si la pregunta tiene dos granularidades, calcula la primera en una subconsulta o CTE y la segunda fuera.
AVG(SUM(total))suele expresar «promedio de totales por grupo», no un único agregado sobre filas originales.- Antes de añadir una etapa, comprueba si la pregunta puede simplificarse, como
SUM(COUNT(*))cuando solo quieresCOUNT(*). - Los mensajes cambian entre motores; la estructura problemática es más importante que memorizar el texto del error.
Conceptos relacionados
Fuentes técnicas
La restricción y el patrón de corrección se contrastaron con documentación primaria de los motores: