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
Semira@gmail.comSemira@gmail.com 

How does batch apex works for trigger to mass update records?

Hi I have written a trigger on a custom object call Job__c which has a lookup relationship to Account. This trigger is updating a field from Job on the Account record when the Job__c is the first related list. However, I just realized trigger only works for each time a job is either inserted or edited. 

How can I update all the existing Accounts with their jobs all at the same time? The first is a date/time field that is updating from Job to Account. I need to run a report for all the Accounts. Now I need to run a batch apex class. '

But what I don't understand is how the batch apex is run. Does it call it trigger class or trigger calls the batch class? I need the batch to happen only once so that all my existing accounts get updated. Then I can keep my trigger active for all the new job that comes in.

I have read the salesforce doc about batch apex and searched multiple times but I'm still not understanding how it works.
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_batch_interface.htm

Here's my working trigger: 
trigger NewJobTimeStampOnAccount on Job__c (after insert, after update) {

  Map<ID, Account> parentAcct = new Map<ID, Account>(); //Making it a map instead of list for easier lookup
  List<Id> listIds = new List<Id>();
 
if(trigger.isInsert || trigger.isUpdate){
  for (Job__c childObj : Trigger.new){
    listIds.add(childObj.Account__c);
   
  }
}
  //Populate the map. Also make sure you select the field you want to update, amount
  //The child relationship is more likely called jobs__r (not job__r) but check
  //You only need to select the child jobs if you are going to do something for example checking whether the job in the trigger is the latest
  parentAcct = new Map<Id, Account>([SELECT id, New_Job_Start_Date__c, (SELECT ID, Date_Time_Taken__c FROM Jobs__r) FROM Account WHERE ID IN :listIds]);
 
  List<Job__c> i = [select id from Job__c where Account__c in :listIds order by Date_Time_Taken__c ASC limit 2];


  for (Job__c job: Trigger.new){
       if(i[0].id == job.id && job.Account__c != null)
     {
        Account myParentAcct = parentAcct.get(job.Account__c);
        if(myParentAcct != null)
        myParentAcct.New_Job_Start_Date__c = job.Date_Time_Taken__c;
       
     }
  }
  update parentAcct.values();
 

}

Please help me understand how should I be writing my batch class? How does batch class updated multiple records at the same time or how does it know to update ALL the accounts in my org at the same time? 

Best Answer chosen by Semira@gmail.com
Chidambar ReddyChidambar Reddy
Hi semira,

There is one way to do this without writing batch apex (not necessary)

create on check box in Account 'IsUpdated', In your trigger add the bold line shown below

for (Job__c job: Trigger.new){
       if(i[0].id == job.id && job.Account__c != null)
     {
        Account myParentAcct = parentAcct.get(job.Account__c);
        if(myParentAcct != null)
        myParentAcct.New_Job_Start_Date__c = job.Date_Time_Taken__c;
        myParentAcct.IsUpdated__c =true;
      
     }
  }
  update parentAcct.values();

Go to Developer console 
Debug> Open Execute Anonymous window (ctrl +E)

Write some code

List<Job__c> jobs = new List<Job__c>();

system.debug('Accounts pending :'+[select Count(id) from Account where IsUpdate__c = false][0].get('expr0'));

for(Account a :[SELECT id(SELECT ID FROM Jobs__r order by Date_Time_Taken__c ASC limit 1) FROM Account where IsUpdated__c = false LIMIT 5000]){

   
         
       jobs.add(a.Jobs__r[0]);
   

}

if(jobs.size()>0) update jobs;
Repeat executing above code snippet untill you see in the debug log 'Accounts Pending : 0'


This is an easy process, I mentioned the limit as 5000 because there can be 10000 DML operations in one Apex Invocation. 5000 are jobs and 5000 are accounts that are updated in trigger.




Thank you
Choose it as best answer if it resolves your issue


All Answers

Ramu_SFDCRamu_SFDC
Hi The below post might help

http://salesforce.stackexchange.com/questions/920/why-use-batch-apex
Chidambar ReddyChidambar Reddy
Hi Semira,

I observed that you need to update all the records only once. In that case you do not need batch class.

I suggest you to export records (With Ids) either from report or through dataloader.

and then to mass update of all the records (using dataloader) , it is better to make a batch size below 120(datloader) while updating so that the trigger will be processed while updating.


I also suggest you to replace List<id> listids with Set<id> setids to deal with duplicates.



I suggest you to export child records along with accountId and remove duplicates based on AccountId in excel, so that only one child of Account will get updated though your requirement will be processed.


Thank you
Choose it as best answer if it resolves your issue

Semira@gmail.comSemira@gmail.com
Chidambar, 

well, I'm trying to avoid manual work. If I were to update through data loader, I will manually have to fetch ALL the child records along with the Account ID. Then find which one is the first child record. Take the date/time and update it on Account. 

Since I have massive amounts of account (duplicates and child accounts as well), this is going to take very long time and not going to be most efficient. This means writing bunch of excel equations and deleting multiple child records which are not the first record on that account from the spreadsheet. 

Isn't there any other way to do it? 
Chidambar ReddyChidambar Reddy
Hi semira,

There is one way to do this without writing batch apex (not necessary)

create on check box in Account 'IsUpdated', In your trigger add the bold line shown below

for (Job__c job: Trigger.new){
       if(i[0].id == job.id && job.Account__c != null)
     {
        Account myParentAcct = parentAcct.get(job.Account__c);
        if(myParentAcct != null)
        myParentAcct.New_Job_Start_Date__c = job.Date_Time_Taken__c;
        myParentAcct.IsUpdated__c =true;
      
     }
  }
  update parentAcct.values();

Go to Developer console 
Debug> Open Execute Anonymous window (ctrl +E)

Write some code

List<Job__c> jobs = new List<Job__c>();

system.debug('Accounts pending :'+[select Count(id) from Account where IsUpdate__c = false][0].get('expr0'));

for(Account a :[SELECT id(SELECT ID FROM Jobs__r order by Date_Time_Taken__c ASC limit 1) FROM Account where IsUpdated__c = false LIMIT 5000]){

   
         
       jobs.add(a.Jobs__r[0]);
   

}

if(jobs.size()>0) update jobs;
Repeat executing above code snippet untill you see in the debug log 'Accounts Pending : 0'


This is an easy process, I mentioned the limit as 5000 because there can be 10000 DML operations in one Apex Invocation. 5000 are jobs and 5000 are accounts that are updated in trigger.




Thank you
Choose it as best answer if it resolves your issue


This was selected as the best answer