Início › Desafios › Desafio 01
Somar 200 contas com uma única consulta
Este é o erro que mais aparece em code review de gente que está começando, e o que mais derruba automação em produção. O código passa no teste manual com três registros, sobe, e na primeira importação de dados a org devolve um erro que ninguém entende. A diferença entre o código que sobrevive e o que quebra costuma ser uma linha fora do lugar.
O cenário
A área comercial quer uma tela que mostre, para um grupo de contas selecionadas, quanto cada uma já fechou em oportunidades ganhas. O time recebeu essa demanda e entregou um método que percorre a lista de contas e, para cada uma, faz uma consulta buscando as oportunidades daquela conta.
Funcionou na apresentação, com três contas escolhidas na mão. Uma semana depois, a tela passou a ser usada em cima de uma lista de 200 contas e começou a estourar o limite de consultas da org. O motivo é simples: uma consulta por conta significa 200 consultas, e o Salesforce permite 100 por transação.
Sua missão é entregar a versão que aguenta qualquer volume.
O que você deve construir
Crie uma classe Apex chamada CarteiraAnalyzer com um método público e
estático, exatamente com esta assinatura:
public static Map<Id, Decimal> totalGanhoPorConta(List<Id> contaIds)
O método deve:
- Receber uma lista de Ids de
Account. - Somar o campo
AmountdasOpportunitydessas contas que estejam no estágioClosed Won. - Devolver um
Mapem que a chave é o Id da conta e o valor é o total somado.
Regras de aceite
São elas que definem se o desafio foi resolvido. Leia com atenção, porque duas delas são o coração do exercício:
- Exatamente uma consulta SOQL, não importa se a lista tem 1 ou 200 contas. Nenhuma consulta dentro de laço.
- Conta sem nenhuma oportunidade ganha precisa aparecer no resultado com valor zero.
Ela não pode simplesmente faltar no
Map, senão quem consome o método é obrigado a tratar ausência de chave. - Lista vazia ou nula devolve um
Mapvazio, sem erro e sem consultar nada. - O método não lança exceção em nenhum cenário previsto acima.
- Oportunidade com
Amountem branco não deve contaminar a soma.
O que este desafio avalia
- Se você tira a consulta de dentro do laço, que é a base da bulkificação.
- Se você conhece agregação em SOQL e sabe ler um
AggregateResult. - Se você pensa no contrato do método, e não só no caminho feliz.
- Se você trata entrada vazia antes de gastar uma consulta.
Travei. Me dá uma direção (sem entregar a resposta)
Três perguntas que destravam o desafio:
- Existe uma forma de pedir ao SOQL que ele já traga a soma agrupada por conta, em vez de trazer todas as oportunidades para você somar no Apex? Pesquise por "SOQL aggregate functions GROUP BY".
- Quando você usa função de agregação, o retorno não é mais uma lista de objetos normais. Pesquise por "Apex AggregateResult example" e veja como dar um apelido para a coluna somada.
- Se o resultado da consulta só traz contas que têm oportunidade, como garantir que as
outras também apareçam no
Mapcom zero? Pense em qual estrutura você monta antes de rodar a consulta.
Sobre passar a lista para a consulta: procure "SOQL bind variable IN clause List". O bind é o que faz a mesma consulta valer para 1 ou 200 ids.
Como testar
Abra o Execute Anonymous na sua org de desenvolvimento e rode o código abaixo. Ele pega cinco contas quaisquer da org e chama o seu método:
// Pega 5 contas quaisquer da org para servir de entrada
List<Id> ids = new List<Id>();
for (Account a : [SELECT Id FROM Account LIMIT 5]) {
ids.add(a.Id);
}
Map<Id, Decimal> resultado = CarteiraAnalyzer.totalGanhoPorConta(ids);
System.debug('Contas consultadas: ' + ids.size());
System.debug('Chaves no resultado: ' + resultado.size());
System.debug('Resultado: ' + resultado);
// Checagem das regras de aceite
System.assertEquals(ids.size(), resultado.size(), 'Toda conta da entrada deve estar no resultado');
System.assertEquals(0, CarteiraAnalyzer.totalGanhoPorConta(new List<Id>()).size(), 'Lista vazia deve voltar Map vazio');
System.assertEquals(0, CarteiraAnalyzer.totalGanhoPorConta(null).size(), 'Lista nula deve voltar Map vazio');
System.debug('Consultas usadas: ' + Limits.getQueries() + ' de ' + Limits.getLimitQueries());
Repare na última linha: ela mostra quantas consultas a transação gastou. É a forma mais direta de provar que você respeitou a regra principal. Se aparecer um número alto ali, tem consulta dentro de laço.
Resolveu? O ganho aqui não é o método em si: é sair com o reflexo de olhar todo laço e perguntar "o que acontece se isso rodar com 200 registros". Esse reflexo é o que separa código que passa em revisão de código que volta.