0

My Batch class logic is:

global class batch_update_Daily_Contacts implements Database.Batchable<sObject>,schedulable
  
    {  
 
    Set<Id> ContactIds = New Set<Id>();
    set<Id> OppIds = New Set<Id>();
    Set<Id> CaseContactIds = New Set<Id>();
    Set<Id> WhoIds = New Set<Id>();
    List<Contact> conList = new List<Contact>();
   
    
    global Database.QueryLocator start(Database.BatchableContext BC)
    {  
        string query = 'SELECT Id FROM Contact where Validate_Retention__c =false';
        if(query != null)
        {
            system.debug('inside if');
            return Database.getQueryLocator(query);
        }
        else
        return null;
    }
   
    
    global void execute(Database.BatchableContext BC,List<Contact> scope)
    {
        for(Contact ConObj : Scope){
            ContactIds.add(ConObj.Id);
            system.debug('This is ContactIds: ' +ContactIds);
           
        }
        if(ContactIds != null){
            for(Case caseObj : [SELECT ID,ContactId FROM Case Where  (ContactId IN : ContactIds) OR (CreatedDate = TODAY)]){
                CaseContactIds.add(caseObj.ContactId);
                system.debug('This is CaseContactIds: ' +CaseContactIds);
             
            }
            for(Task taskObj : [SELECT ID,WhoID FROM Task Where ( WhoID IN : ContactIds AND Who.Type = 'Contact') Or (CreatedDate = TODAY) ])
            {
                WhoIds.add(taskObj.WhoID);
                system.debug('This is WhoIds: ' +WhoIds);
                
            }
             for(Opportunity oppObj : [select ContactId from Opportunity where Last_Donation__c = TODAY AND ContactId IN : ContactIds])
            {
                OppIds.add(oppObj.ContactID);
                system.debug('This is OppIds: ' +OppIds);
                
            }
        }
        if(CaseContactIds != null || WhoIds != null || OppIds != null){
            CaseContactIds.addAll(WhoIds);
            CaseContactIds.addAll(OppIds);
            system.debug('This is CaseContactIds: ' +CaseContactIds);
            
        }
        for(Contact ConObj : [SELECT ID FROM Contact WHERE ID IN : CaseContactIds]){
            system.debug('This is ConObj: ' +ConObj);
            ConObj.Validate_Retention__c = true;
            ConList.add(ConObj);
        }
        if(ConList != null){
            update ConList;
        }
     }
       
              global void finish(Database.BatchableContext BC)
        {  
                
        }
   
    public void execute(SchedulableContext sc)
    {
        
    }
    }

After running, I am getting:

"First error: Apex CPU time limit exceeded"

Can someone help here?

New contributor
Shweta Deshpande is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
0

There are a number of problems with this code that I don't think I can cover in a single answer, but I want to highlight a couple of specific points and then offer a general suggestion for what a good form of this code might look like.

Key Points

  • Your logic in this code does not match your stated objective. There are no N-year mechanics here, and the queries that filter on Created Date appear to simply be wrong (they will include records unrelated to scope).
  • The most likely source of the CPU time limit is that you're pulling in a very large number of irrelevant records to iterate over.
  • You are performing a large number of null checks that are irrelevant because variables that you explicitly initialize will never be null.
  • You should move your instance variables that are used only within execute() into the scope of that method. They are not functionally instance variables and this is likely to lead to confusion.
  • Your batch class almost certainly should not be global.

Suggestions for Rebuilding

Your execute() method should not run queries against Case, Opportunity, and Task. Instead, you should run a query against Contact - against the Contact records that are in scope - to acquire details of these related objects through parent-child subqueries, which return only the records related to each Contact. You can use ORDER BY and LIMIT clauses in your subqueries to get back, for example, only the most recent Opportunity for a given Contact.

Additionally, you may want to use the LastActivityDate field on Contact to avoid having to explicitly query the Task object.

For example, you might do something like

List<Contact> contactList = [
    SELECT Id, 
        (SELECT Id, Last_Donation__c FROM Opportunities ORDER BY Last_Donation__C DESC LIMIT 1), 
    // Add more subqueries here for Case and optionally Task
    FROM Contact
    WHERE Id IN :scope
]; 
for (Contact c : contactList) {
    if (c.Opportunities.size() > 0 && c.Opportunities.get(0).Last_Donation__c < SOME_DATE) {
         // Do action here. You'd need to also write logic on the other fields
         // and relationships that govern your action.
    }
}

update contactList;

This is a very rough sketch. Please do not copy and paste this code exactly as shown here; you'll need to do extensive work to incorporate your specific requirements and data model.

Your Answer

Shweta Deshpande is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.