Bulkificación
Escribir código que hace el mismo número de consultas y de DML con un registro que con doscientos. La receta es siempre la misma: juntar, consultar una vez, trabajar en memoria, guardar una vez.
✓ En esta sub-lección
- 01Reconocer consultas y DML dentro de bucles y explicar por qué fallan con volumen.
- 02Aplicar la receta juntar → consultar una vez → trabajar en memoria → guardar una vez.
- 03Evitar Ids duplicados en una lista de update usando Set o Map.
La nutria: ¿Te acuerdas? · Repaso de la lección 2
Con Database.insert(leads, false), si uno de 200 falla, ¿qué pasa?
Casi todo el código de este curso funciona con un registro. La pregunta que separa el código de práctica del código de producción es otra: ¿funciona con doscientos a la vez? En Salesforce, esa pregunta no es teórica, porque doscientos a la vez es lo normal.
La nutria: El paralelo de Admin
El código que funciona… hasta que no
// Marcar como Hot las cuentas de las oportunidades ganadas hoy
List<Opportunity> wonToday = [SELECT Id, AccountId FROM Opportunity
WHERE IsWon = true AND CloseDate = TODAY];
for (Opportunity o : wonToday) {
Account acc = [SELECT Id, Rating FROM Account WHERE Id = :o.AccountId]; // ❌ consulta en el bucle
acc.Rating = 'Hot';
update acc; // ❌ DML en el bucle
}Este código pasa cualquier prueba hecha a mano, porque a mano ganas una oportunidad cada vez. El día que Ventas cierra el trimestre con una carga masiva, falla. Y falla entero: la no se puede capturar, así que se deshace todo lo que había hecho la transacción, incluido lo que guardaron los flows.
La receta en cuatro pasos
for (Opportunity o : wonToday) {
Account a = [SELECT … WHERE Id = :o.AccountId];
a.Rating = 'Hot';
update a;
}Con 3: 4 consultas y 3 DML. Funciona… hasta el cierre de trimestre.
List<Opportunity> wonToday = [SELECT Id, AccountId FROM Opportunity
WHERE IsWon = true AND CloseDate = TODAY];
// 1. Juntar: los Ids que necesitas, sin repetir
Set<Id> accountIds = new Set<Id>();
for (Opportunity o : wonToday) {
accountIds.add(o.AccountId);
}
// 2. Consultar una vez
List<Account> accounts = [SELECT Id, Rating FROM Account WHERE Id IN :accountIds];
// 3. Trabajar en memoria
for (Account a : accounts) {
a.Rating = 'Hot';
}
// 4. Guardar una vez
update accounts;La nutria: Flow te enseñó esto primero
La nutria: ¿Y por qué no un Flow? Aquí Flow habría acertado solo
La trampa del Id duplicado
Si dos oportunidades ganadas son de la misma cuenta y vas añadiendo cuentas a una List dentro del bucle, la lista acaba con la misma cuenta dos veces. update de una lista con Ids repetidos falla con «Duplicate id in list». Por eso el paso 1 usa un Set: guarda cada Id una sola vez. Si además necesitas ir modificando los registros mientras recorres, usa un Map<Id, Account>: put con la misma clave sustituye en lugar de duplicar, y al final haces update mapa.values().
Map<Id, Account> toUpdate = new Map<Id, Account>();
for (Opportunity o : wonToday) {
toUpdate.put(o.AccountId, new Account(Id = o.AccountId, Rating = 'Hot'));
}
update toUpdate.values(); // cada cuenta una sola vez, sin haberla consultadoCómo detectar código sin bulkificar
- Un corchete de consulta [SELECT … ] dentro de las llaves de un for o de un while.
- insert, update, delete o upsert dentro de un bucle, o Database.insert/update llamados dentro.
- Un método que recibe un solo registro (Account acc) y hace una consulta o un DML, llamado desde un bucle. Es el mismo error escondido un nivel más abajo: los métodos bien diseñados reciben listas.
La nutria: Bajar el Batch size a 1 no arregla nada