How to code more efficient to avoid "Apex CPU time limit exceeded"?

Use a Map instead:

List<Task> tasks = [SELECT Id ... FROM Task];
Map<Id, Contact> contacts = new Map<Id, Contact>([SELECT Id ... FROM Contact]);

for (Task t : tasks){
    Contact c = contacts.get(t.WhoId);
    if(c != null) {
        //some logic here   
    }
}

This technique was originally used to avoid script limits, and it's equally effective to avoid timeout limits.

Edit

As stated by Mike, you should make sure your filters are also correctly limiting records to avoid heap limits and query limits, and your field list should include only the fields necessary to complete the task.

Task[] tasks = [SELECT Id ... FROM Task WHERE ...];
Map<Id, Contact> contacts = new Map<Id, Contact>();
for(Task record: tasks) {
    if(record.WhoId != null && record.WhoId.getSObjectType() == Contact.SObjectType) {
        contacts.put(record.WhoId, null);
    }
}
contacts.putAll([SELECT Id ... FROM Contact WHERE Id IN :contacts.keySet()]);

The problem stems from your SOQL queries; they're fetching ALL Tasks and ALL Contacts. Here's one way to improve that:

  1. Filter your tasks to only those relevant
  2. Retrieve those WhoId that belong to Contacts by checking their prefix
  3. Fetch the necessary Contacts

    List<Task> tasks = [SELECT Id, WhoId FROM Task WHERE /* INSERT FILTER */];
    List<id> taskWhoId = new List<id>();
    for (Task t : tasks){
      // Only take those matched to a Contact
      if (t.WhoId.startsWith('003'){
        taskWhoId.add(t.WhoId);
      } 
    }
    
    Map<Id,Contact> contacts = new Map<id, Contact>([SELECT Id FROM Contact WHERE ID in :taskWhoId]);    
    for (Task t : tasks){
      if contacts.containsKey(t.WhoId){
        Contact taskContact = contacts.get(t.WhoId);
        // Do something with the Task and Contact
      }
    }
    

As Phil points out, you could also combine this into one query, if you can filter on Contacts instead of Tasks (that depends on your requirements).

Tags:

Apex