Bulkification Apex - Trop de requêtes SOQL: 101
J'ai une classe APEX que j'ai regroupée mais qui me pose toujours des problèmes avec des lots de 200 enregistrements. J'espérais obtenir de l'aide pour rendre l'APEX plus efficace, toute assistance / direction serait grandement appréciée.
J'ai un générateur de processus qui déclenche une classe Apex sur l'objet principal lorsque l'une des conditions suivantes est remplie:
ISCHANGED([Lead].MobilePhone),
ISCHANGED([Lead].Phone),
ISCHANGED([Lead].Home_Phone__c),
ISCHANGED([Lead].Alternate_Phone__c),
ISCHANGED([Lead].Alternate_Phone_2__c),
ISNEW()
Le générateur de processus transmet le [Lead] .Id à la 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());
}
}
}
- Cette classe Apex peut être déclenchée à partir de trois générateurs de processus liés à l'objet Lead / Account / Contact. Ainsi, le générateur de processus appelle une @invocablemethod qui détermine le type d'objet. En fonction du type d'objet, il appellera ensuite une méthode privée.
- Cette méthode privée est updateBulkifiedLeadPhoneTrimFields. Il parcourt le tableau / la liste des enregistrements - recherche les informations appropriées, met à jour l'enregistrement et le pousse vers un nouveau tableau. Une fois la boucle terminée, nous mettons à jour la liste des enregistrements ensemble.
- Les mises à jour qui sont effectuées sont les mêmes dans toutes les méthodes - cela met à jour les champs phonetrim (normaliser les champs téléphone). La méthode appelée est 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;
}
Lors de l'exécution par lots de 200, je reçois l'erreur suivante: An Apex error occurred: System.LimitException: Too many SOQL queries: 101 ---
Des questions)
- Y a-t-il un moyen de rendre cela plus efficace? Si c'est le cas, comment? Mon objectif ultime serait que cela puisse fonctionner par lots de 200 disques.
Mettre à jour
J'ai fait le changement suivant et cela semble avoir fonctionné. Des recommandations sur d'autres mises à jour?
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;
}
Réponses
Le problème est que vous avez des requêtes à l'intérieur d'une boucle.
Dans votre tentative de regroupement, nous voyons ceci
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];
// ...
Étant donné que vous n'avez qu'un seul filtre dans votre requête, il est facile de sortir de la boucle à l'aide de l'approche standard itérer-rassembler-requête.
// 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
}
Cependant, vous n'avez probablement pas besoin de rencontrer autant de problèmes, puisque vous ne travaillez qu'avec 1 SObject à la fois, rien ne devrait vous empêcher de simplement alimenter une boucle for avec une requête qui utilise idsdirectement votre liste. .
for(Lead myLead :[SELECT Id, Field2__c, Field3__c FROM Lead WHERE Id IN :ids]){
// do work
}
Autre que ça...
- vous pouvez envisager d'utiliser la
getSObjectType()méthode de laIdclasse pour rendre votre détection SObject plus lisible- par exemple
SObjectType sobjType = ((Id)ids[0]).getSObjectType();etif(sobjType == Lead.SObjectType)
- par exemple
- Avaler des exceptions est généralement une mauvaise chose. Cela rend plus difficile la détection d'un problème et le débogage du problème une fois que vous savez que vous en avez un. Envisagez d'éliminer toutes vos instructions try / catch