Bulkification Apex - Troppe query SOQL: 101
Ho una classe APEX che ho raggruppato, ma continua a darmi problemi con lotti di 200 record. Speravo di ottenere assistenza per rendere l'APICE più efficiente, qualsiasi assistenza / direzione sarebbe stata molto apprezzata.
Ho un generatore di processi che attiva una classe Apex sull'oggetto principale quando viene soddisfatta una delle seguenti condizioni:
ISCHANGED([Lead].MobilePhone),
ISCHANGED([Lead].Phone),
ISCHANGED([Lead].Home_Phone__c),
ISCHANGED([Lead].Alternate_Phone__c),
ISCHANGED([Lead].Alternate_Phone_2__c),
ISNEW()
Il Process Builder passa il [Lead] .Id alla classe APEX.
public class UpdatePhoneTrimFields {
@InvocableMethod
public static void invocableTrimPhoneNumbers(List<String> ids){
String objectType = ids[0];
if(objectType.startsWithIgnoreCase('00Q')){
updateBulkifiedLeadPhoneTrimFields(ids);
}
else if(objectType.startsWithIgnoreCase('003')){
updateBulkifiedContactPhoneTrimFields(ids);
}
else if(objectType.startsWithIgnoreCase('001')){
updateBulkifiedAccountPhoneTrimFields(ids);
}
else {
system.debug('Unknown Record Type - Cannot Proceed');
}
}
private static Decimal normalizePhoneNumber(String rawPhoneNumber){
try{
if(rawPhoneNumber != null){
return Decimal.ValueOf(rawPhoneNumber.replaceAll('[^0-9]', ''));
}
} catch (Exception e){
System.debug('An Unexpected error occurred trimming a phone number: ' + e.getMessage());
}
return null;
}
private static void updateBulkifiedLeadPhoneTrimFields(List<String> ids){
system.debug('Attempting to Normalize the Phone numbers for a list of ' + ids.size() + ' leads.');
list<Lead> leadsToUpdate = new List<lead>();
for(Id recordId: ids){
String stringRecordId = Id.ValueOf(recordId);
if(stringRecordId.startsWithIgnoreCase('00Q')){
try {
Lead l = [SELECT Phone, MobilePhone, Home_Phone__c, Alternate_Phone__c, Alternate_Phone_2__c, Phone_Trim__c, MobilePhone_Trim__c, Home_Phone_Trim__c, Alternate_Phone_Trim__c, Alternate_Phone_2_Trim__c FROM Lead WHERE Id =: recordId];
l.Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Phone);
l.MobilePhone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.MobilePhone);
l.Home_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Home_Phone__c);
l.Alternate_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Alternate_Phone__c);
l.Alternate_Phone_2_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Alternate_Phone_2__c);
system.debug('Adding Lead to List to Normalize: '+ l);
leadsToUpdate.add(l);
}catch(Exception e){
System.debug('An Unexpected error occurred normalizing Lead Phone Fields: ' + e.getMessage());
}
} else {
System.debug('Cannot Normalize Phone for this record: ' + recordId + '. Not a Lead');
}
}
try {
system.debug('Updading a list of Leads with ' + leadsToUpdate.size() + ' records');
system.debug(leadsToUpdate);
update leadsToUpdate;
} catch (Exception e) {
System.debug('An Unexpected error occurred normalizing Lead Phone Fields: ' + e.getMessage());
}
}
private static void updateBulkifiedContactPhoneTrimFields(List<String> ids){
system.debug('Attempting to Normalize the Phone numbers for a list of ' + ids.size() + ' Contacts.');
list<Contact> contactsToUpdate = new List<Contact>();
for(Id recordId: ids){
String stringRecordId = Id.ValueOf(recordId);
if(stringRecordId.startsWithIgnoreCase('003')){
try{
Contact c = [SELECT Phone, MobilePhone, HomePhone, OtherPhone, Phone_Trim__c, MobilePhone_Trim__c, HomePhone_Trim__c, OtherPhone_Trim__c FROM Contact Where Id =: recordId];
c.Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(c.Phone);
c.MobilePhone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(c.MobilePhone);
c.HomePhone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(c.HomePhone);
c.OtherPhone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(c.OtherPhone);
system.debug('Adding Contact to List to Normalize: '+ c);
contactsToUpdate.add(c);
}catch(Exception e){
System.debug('An Unexpected error occurred normalizing Lead Phone Fields: ' + e.getMessage());
}
}else {
System.debug('Cannot Normalize Phone for this record: ' + recordId + '. Not a Contact');
}
}
try {
system.debug('Updating List of Contacts with ' + contactsToUpdate.size() + ' records');
system.debug(contactsToUpdate);
update contactsToUpdate;
} catch (Exception e) {
System.debug('An Unexpected error occurred trimming Contact Phone Fields: ' + e.getMessage());
}
}
private static void updateBulkifiedAccountPhoneTrimFields(List<String> ids){
system.debug('Attempting to Normalize the Phone numbers for a list of ' + ids.size() + ' Accounts.');
list<Account> accountsToUpdate = new List<Account>();
for(Id recordId: ids){
String stringRecordId = Id.ValueOf(recordId);
if(stringRecordId.startsWithIgnoreCase('001')){
try{
Account a = [SELECT Phone, Alternate_Phone__c, Alternate_Phone_2__c, Phone_Trim__c, Alternate_Phone_Trim__c, Alternate_Phone_2_Trim__c FROM Account WHERE Id =: recordId];
a.Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(a.Phone);
a.Alternate_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(a.Alternate_Phone__c);
a.Alternate_Phone_2_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(a.Alternate_Phone_2__c);
system.debug('Adding Account to List to Normalize: '+ a);
accountsToUpdate.add(a);
}catch(Exception e) {
System.debug('An Unexpected error occurred normalizing Account Phone Fields: ' + e.getMessage());
}
} else {
System.debug('Cannot Normalize Phone for this record: ' + recordId + '. Not an Account');
}
}
try {
system.debug('Updating List of Accounts with ' + accountsToUpdate.size() + ' records');
system.debug(accountsToUpdate);
update accountsToUpdate;
} catch (Exception e) {
System.debug('An Unexpected error occurred trimming Account Phone Fields: ' + e.getMessage());
}
}
}
- Questa classe Apex può essere attivata da tre process builder relativi all'oggetto lead / account / contatto. Quindi il generatore di processi chiama un @invocablemethod che determina il tipo di oggetto. In base al tipo di oggetto, chiamerà quindi un metodo privato.
- Questo metodo privato è updateBulkifiedLeadPhoneTrimFields. Passa attraverso l'array / elenco di record: cerca le informazioni appropriate, aggiorna il record e lo invia a un nuovo array. Una volta terminato il ciclo, aggiorniamo insieme l'elenco dei record.
- Gli aggiornamenti che vengono eseguiti sono gli stessi in tutti i metodi: questo si aggiorna ai campi phonetrim (normalizza i campi phone). Il metodo chiamato è normalizePhoneNumber
updateBulkifiedLeadPhoneTrimFields
private static void updateBulkifiedLeadPhoneTrimFields(List<String> ids){
system.debug('Attempting to Normalize the Phone numbers for a list of ' + ids.size() + ' leads.');
list<Lead> leadsToUpdate = new List<lead>();
for(Id recordId: ids){
String stringRecordId = Id.ValueOf(recordId);
if(stringRecordId.startsWithIgnoreCase('00Q')){
try {
Lead l = [SELECT Phone, MobilePhone, Home_Phone__c, Alternate_Phone__c, Alternate_Phone_2__c, Phone_Trim__c, MobilePhone_Trim__c, Home_Phone_Trim__c, Alternate_Phone_Trim__c, Alternate_Phone_2_Trim__c FROM Lead WHERE Id =: recordId];
l.Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Phone);
l.MobilePhone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.MobilePhone);
l.Home_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Home_Phone__c);
l.Alternate_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Alternate_Phone__c);
l.Alternate_Phone_2_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Alternate_Phone_2__c);
system.debug('Adding Lead to List to Normalize: '+ l);
leadsToUpdate.add(l);
}catch(Exception e){
System.debug('An Unexpected error occurred normalizing Lead Phone Fields: ' + e.getMessage());
}
} else {
System.debug('Cannot Normalize Phone for this record: ' + recordId + '. Not a Lead');
}
}
try {
system.debug('Updading a list of Leads with ' + leadsToUpdate.size() + ' records');
system.debug(leadsToUpdate);
update leadsToUpdate;
} catch (Exception e) {
System.debug('An Unexpected error occurred normalizing Lead Phone Fields: ' + e.getMessage());
}
}
normalizePhoneNumber
private static Decimal normalizePhoneNumber(String rawPhoneNumber){
try{
if(rawPhoneNumber != null){
return Decimal.ValueOf(rawPhoneNumber.replaceAll('[^0-9]', ''));
}
} catch (Exception e){
System.debug('An Unexpected error occurred trimming a phone number: ' + e.getMessage());
}
return null;
}
Quando si esegue in batch da 200 ricevo il seguente errore: An Apex error occurred: System.LimitException: Too many SOQL queries: 101 ---
Domande)
- C'è un modo per renderlo più efficiente? Se é cosi, come? Il mio obiettivo finale sarebbe che questo sia in grado di funzionare in batch di 200 record.
Aggiornare
Ho apportato la seguente modifica e sembra che abbia funzionato. Qualche consiglio su ulteriori aggiornamenti?
system.debug('Attempting to Normalize the Phone numbers for a list of ' + ids.size() + ' leads.');
List<Lead> leadsToUpdate = [SELECT Id, Phone, MobilePhone, Home_Phone__c, Alternate_Phone__c, Alternate_Phone_2__c, Phone_Trim__c, MobilePhone_Trim__c, Home_Phone_Trim__c, Alternate_Phone_Trim__c, Alternate_Phone_2_Trim__c FROM Lead WHERE Id =: ids];
for(Lead l: leadsToUpdate){
try{
l.Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Phone);
l.MobilePhone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.MobilePhone);
l.Home_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Home_Phone__c);
l.Alternate_Phone_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Alternate_Phone__c);
l.Alternate_Phone_2_Trim__c = UpdatePhoneTrimFields.normalizePhoneNumber(l.Alternate_Phone_2__c);
} catch(Exception e){
System.debug('An Unexpected error occurred normalizing Lead with Id: ' + l.Id + '. Error' + e.getMessage());
}
}
system.debug('Updading a list of Leads with ' + leadsToUpdate.size() + ' records');
system.debug(leadsToUpdate);
update leadsToUpdate;
}
Risposte
Il problema è che hai query all'interno di un ciclo.
Nel tuo tentativo di raggruppamento, vediamo questo
for(Id recordId: ids){
String stringRecordId = Id.ValueOf(recordId);
if(stringRecordId.startsWithIgnoreCase('00Q')){
try {
// This query is inside of a loop
// That's a problem
Lead l = [SELECT Phone, MobilePhone, Home_Phone__c, Alternate_Phone__c, Alternate_Phone_2__c, Phone_Trim__c, MobilePhone_Trim__c, Home_Phone_Trim__c, Alternate_Phone_Trim__c, Alternate_Phone_2_Trim__c FROM Lead WHERE Id =: recordId];
// ...
Poiché hai un solo filtro nella tua query, è facile uscire dal ciclo usando l'approccio standard iterate-gather-query.
// First, set up a collection to hold the data you want to query against
Set<Id> recordIds = new Set<Id>();
// Iterate over your records to gather the data
for(SomeObject__c someObj :trigger.new){
// gather your data
recordIds.add(someObj.Relationship_Field__c);
}
// Query and store for later use
Map<Id, SObject> sobjMap = new Map<Id, SObject>([SELECT Id, Field1__c, Field2__c FROM OtherObject__c WHERE Id IN :recordIds);
// Iterate over the records again
// This time, you can pull the related data from the map you made earlier
for(SomeObject__c someObj :trigger.new){
OtherObject__c relatedRecord = sobjMap.get(someObj.Relationship_Field__c);
// do other work
}
Probabilmente non è necessario affrontare così tanti problemi, dato che stai lavorando solo con 1 SObject alla volta, non dovrebbe esserci nulla che ti impedisca di alimentare semplicemente un ciclo for con una query che utilizza idsdirettamente il tuo elenco .
for(Lead myLead :[SELECT Id, Field2__c, Field3__c FROM Lead WHERE Id IN :ids]){
// do work
}
Oltre a quello...
- potresti prendere in considerazione l'utilizzo del
getSObjectType()metodo dellaIdclasse per rendere più leggibile il rilevamento di SObject- ad esempio
SObjectType sobjType = ((Id)ids[0]).getSObjectType();eif(sobjType == Lead.SObjectType)
- ad esempio
- Ingoiare le eccezioni è generalmente una cosa negativa. Rende più difficile rilevare quando si verifica un problema e rende più difficile eseguire il debug del problema una volta che si sa di averne uno. Considera l'idea di eliminare tutte le tue dichiarazioni try / catch