Funciones de agregación
Contar, sumar, promediar y agrupar dentro de la base de datos: te llega el total, no las diez mil filas que lo forman.
✓ En esta sub-lección
- 01Contar registros con COUNT() sin traerlos.
- 02Agrupar con GROUP BY y leer SUM, AVG, MIN y MAX desde AggregateResult.
- 03Distinguir WHERE (filtra filas antes de agrupar) de HAVING (filtra grupos después).
La nutria: ¿Te acuerdas? · Repaso de la lección 5
¿Cómo filtras por una variable accId en SOQL?
Muchas preguntas de negocio no piden registros sino números: cuántos casos abiertos hay, cuánto pipeline tiene cada etapa, cuál es el ticket medio. Podrías traer todas las oportunidades y sumarlas en un bucle, pero sería como exportar el informe a Excel para sumar una columna que el propio informe ya sabía sumar.
La nutria: El paralelo de Admin
Contar: COUNT()
Integer openCases = [SELECT COUNT() FROM Case WHERE IsClosed = false];
System.debug('Casos abiertos: ' + openCases);Agrupar: GROUP BY y AggregateResult
List<AggregateResult> byStage = [
SELECT StageName, COUNT(Id) deals, SUM(Amount) total, AVG(Amount) average
FROM Opportunity
WHERE IsClosed = false
GROUP BY StageName
];
for (AggregateResult ar : byStage) {
String stage = (String) ar.get('StageName');
Integer deals = (Integer) ar.get('deals');
Decimal total = (Decimal) ar.get('total');
System.debug(stage + ': ' + deals + ' oportunidades, ' + total);
}SELECT StageName, COUNT(Id) n, SUM(Amount) total
FROM Opportunity WHERE IsClosed = false
GROUP BY StageName
HAVING COUNT(Id) >= 3- Prospecting · 30.000
- Proposal · 90.000
- Prospecting · 25.000
- Negotiation · 180.000
- Proposal · 70.000
- Prospecting · 35.000
- Negotiation · 130.000
- Proposal · 80.000
Paso 1 de 4
Ocho oportunidades abiertas sueltas. Sin agregado, las ocho viajarían a Apex y tendrías que sumar tú en un bucle.
- Con GROUP BY ya no recibes Opportunity, sino AggregateResult: una fila por grupo, no por registro.
- Cada total lleva un alias (deals, total, average) escrito justo detrás de la función. Con ese alias lo lees: ar.get('total'). Sin alias, Salesforce los llama expr0, expr1… y tu código se vuelve ilegible.
- get() devuelve Object, el tipo comodín. Para usarlo como número haces el casting del Módulo 1: (Decimal) ar.get('total').
- Todo campo del SELECT tiene que estar agrupado o dentro de una función. Pedir Name aquí no compila: ¿el nombre de cuál de las cuarenta oportunidades del grupo?
WHERE antes, HAVING después
WHERE decide qué filas entran en los grupos. HAVING decide qué grupos salen, mirando sus totales. En un informe de resumen no hay forma sencilla de decir «enséñame solo las etapas con más de 100.000»: lo miras a ojo. HAVING lo hace por ti.
// Cuentas con más de 5 casos abiertos: candidatas a revisión de servicio
List<AggregateResult> busy = [
SELECT AccountId, COUNT(Id) openCases
FROM Case
WHERE IsClosed = false // filas: solo casos abiertos
GROUP BY AccountId
HAVING COUNT(Id) > 5 // grupos: solo cuentas con más de 5
ORDER BY COUNT(Id) DESC
];| Función | Qué calcula | En el informe o roll-up |
|---|---|---|
| COUNT() / COUNT(Id) | Cuántos registros | Recuento de registros · roll-up COUNT |
| COUNT_DISTINCT(campo) | Cuántos valores distintos | Recuento único |
| SUM(campo) | La suma | Suma · roll-up SUM |
| AVG(campo) | La media | Promedio |
| MIN / MAX(campo) | El menor / el mayor | Mín / Máx · roll-up MIN / MAX |
La nutria: ¿Y por qué no un Flow? Porque Get Records no agrupa