function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
K.G.K.G. 

Help Scheduling/Batching Apex Code

Dear Developer community,
I wrote the big code block at the bottom of this post, and it works nicely (in a sandbox with 20 Contacts) from the following call:
ScheduledMiscellany.updateHasOppTypeGAOnContact((new Map<Id,SObject>([SELECT Id FROM Contact])).keySet());
Now I want to run it at midnight every night.  And since my production environment has over 100,000 Contacts - so I need to break up the processing into chunks.
(Right now, it will be our 1st scheduled bit of code, but it might not be the last once I know what I'm doing.)

I am pretty mind-boggled by the documentation I'm reading on batch and scheduling - I am just not figuring out how to do it.

Would anyone mind showing me how to do it with my code?

Thanks!
-K

 
public class ScheduledMiscellany {   
    
    public static void updateHasOppTypeGAOnContact(Set<Id> cIdSet) {
        // Indicate that this method's execution is starting
        System.debug('START: ScheduledMiscellany.updateGAOppCountsOnContact()');
        
        Id defaultOppRecordTypeID = [SELECT Id FROM RecordType 
                                     WHERE RecordType.DeveloperName = 'Graduate_Admissions' 
                                     AND RecordType.SObjectType = 'Opportunity' LIMIT 1].Id;
        if (defaultOppRecordTypeID == NULL) {
            System.debug('WARNING: Opportunity query would fail. Unable to find opportunity record type ( Graduate Admissions ).');
            return;      // Do not proceed.
        }
        
        // Set up a map to hold the values to write
        Map<Id, Boolean> contactsAndValuesMap = new Map<Id, Boolean>();
        
        // Pull a SOQL query showing all Contacts with an un-checked checkbox that needs to get checked
        List<AggregateResult> gaNeedsChangeToTrue = [SELECT ContactID
                                                     FROM OpportunityContactRole
                                                     WHERE IsPrimary = true
                                                     AND IsDeleted=false 
                                                     AND Opportunity.RecordTypeID = :defaultOppRecordTypeID 
                                                     AND Opportunity.IsDeleted = false 
                                                     AND ContactID IN :cIdSet
                                                     AND Contact.Has_Any_Opportunities_Grad_Admissions__c = false 
                                                     GROUP BY ContactID 
                                                     HAVING COUNT(ID) > 0];
        
        
        // Pull a different query showing all Contacts with a checked checkbox that needs to get un-checked
        List<Contact> gaNeedsChangeToFalse = [SELECT Id
                                              FROM Contact
                                              WHERE Has_Any_Opportunities_Grad_Admissions__c = true 
                                              AND Id IN :cIdSet
                                              AND Id NOT IN (SELECT ContactId 
                                                             FROM OpportunityContactRole 
                                                             WHERE IsPrimary = true
                                                             AND IsDeleted=false 
                                                             AND Opportunity.RecordTypeID = :defaultOppRecordTypeID 
                                                             AND Opportunity.IsDeleted = false)];
        
        // Iterate through the two lists, casting their parts to new data types and setting the values of the Map defined above.
        for (AggregateResult x : gaNeedsChangeToTrue) { contactsAndValuesMap.put((Id)x.get('ContactId'), true); }
        for (Contact x : gaNeedsChangeToFalse) { contactsAndValuesMap.put((Id)x.get('Id'), false); }
        
        // Create a list of all Contact objects needing a change
        List<Contact> cs = [SELECT Id, Has_Any_Opportunities_Grad_Admissions__c FROM Contact 
                                    WHERE IsDeleted = false
                                    AND Id IN :contactsAndValuesMap.keySet()];
        
        
        
        // Update the list of Contact objects with their values from the Map (or 0 if Contact not in the map)
        System.debug('About to start the for loop that assigns true/false to Has_Any_Opportunities_Grad_Admissions__c');
        for (Contact c : cs) {
            if (contactsAndValuesMap.get(c.Id) == null) {
                System.debug('Unexpected error - Contact no longer exists');
            } else {
                System.debug('Has_Any_Opportunities_Grad_Admissions__c for ' + c.Id + ' is ' + c.Has_Any_Opportunities_Grad_Admissions__c);
                System.debug('Setting the true/false ' + c.Id + ' to be ' + contactsAndValuesMap.get(c.Id));
                c.Has_Any_Opportunities_Grad_Admissions__c = contactsAndValuesMap.get(c.Id);
                System.debug('Has_Any_Opportunities_Grad_Admissions__c for ' + c.Id + ' is ' + c.Has_Any_Opportunities_Grad_Admissions__c);
            }
            System.debug('Just ended the for loop that assigns true/false to Has_Any_Opportunities_Grad_Admissions__c');
        }

        
        // Save changes to database
        System.debug('About to start DML to the list of Contacts');
        try {
            UPDATE cs;
            System.debug('Database Confirmation: Update to list of Contacts succeeded.');
        }
        catch (System.DmlException e) {
            System.debug('Database Error: Update failed.');
            for (Integer i = 0; i < e.getNumDml(); i++) {
                System.debug(e.getDmlMessage(i)); 
            }
        }
        System.debug('Just ended the DML to the list of Contacts');
        
        // Indicate that this method's execution is ending
        System.debug('END: ScheduledMiscellany.updateGAOppCountsOnContact()');
    }
}




 
Swayam@SalesforceGuySwayam@SalesforceGuy
Hi,


First you need to  put the above code in Batch Class, Below is sample of batch class :
 
global class ExampleBatchClass implements Database.Batchable<sObject>{

        global ExampleBatchClass(){
                   // Batch Constructor
        }
       
        // Start Method
        global Database.QueryLocator start(Database.BatchableContext BC){
         return Database.getQueryLocator(query);
        }
      
      // Execute Logic
       global void execute(Database.BatchableContext BC, List<sObject>scope){
              // Logic to be Executed batch wise      
     
       }
     
       global void finish(Database.BatchableContext BC){
            // Logic to be Executed at finish
       }
    }

Then Schedule the above class using  this :
 
global class Scheduler_class implements Schedulable{

    public static String sched = '0 00 00 * * ?';  //Every Day at Midnight 

    global static String scheduleMe() {
        Scheduler_class SC = new Scheduler_class(); 
        return System.schedule('My batch Job', sched, SC);
    }

    global void execute(SchedulableContext sc) {
        ExampleBatchClassb1 = new ExampleBatchClass();
        ID batchprocessid = Database.executeBatch(b1,50);           
    }
}

Hope, this will help, let me know, if you have any doubt.

--
Thanks,
Swayam
@salesforceguy