Being a Salesforce developer, writing a trigger would be a part and parcel of your every day work. Writing a trigger is not really a big deal. Once you have your command over the apex programming language, you can easily write one. However, writing a trigger which is bulkified is a task in itself. The governor limits that Salesforce has enforced will easily land you up inside a plethora of exceptions if your trigger is not bulkified. So, how do you go about writing a trigger which is bulkified and efficient enough to handle bulk records. I have tried my best to address the mistakes that most novice developers make while writing trigger below.

There are a couple of considerations you need to keep in your mind while writing a trigger which would process a bulk set of records.

P.S: A trigger is always and always written with the assumption that it needs to handle bulk set of records ! ! !

So, irrespective of your need to write a trigger for a particular requirement, you need to ensure that your trigger is bulkified.

So, below are the best practices to write a bulkified trigger.

  1. Never ever write a SOQL query inside any “for” loop for whatsoever reason. If you do that, your trigger is guaranteed to hit the governor limits.
  2. If you need to fetch large amount of data from an object (More than 50,000 records), then go for batch apex to pull that data, else you will again bump into another governor limit that wouldn’t allow you to fetch more than 50,000 records.
  3. Never ever perform a DML operation inside a “for” loop.
  4. Do not fetch unnecessary data. Only fetch those fields and objects in your SOQL that you really require.

The above 4 points are a must for your trigger to work efficiently. The beauty of writing a trigger lies in the fact that it should be able to process the records smoothly irrespective of whether the number of records are in millions, thousands or in units. The processing of the trigger should be equally good in all scenarios and conditions.

We will now walk through an example wherein I will show you how to optimize a trigger which was written poorly.

The below trigger has been written on the “Account” object to ensure that the “Contacts” associated with each Account are updated every time the corresponding Account record is updated.


trigger accountTestTrggr on Account (before insert, before update) {

   //For loop to iterate through all the incoming Account records
   for(Account a: Trigger.new){

      List<Contact> contacts = [select id, salutation, firstname, lastname, email
      from Contact where accountId = :a.Id];

      for(Contact c: contacts){

         c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;

         update c;
      }
   }
}

 

There are three major blunders committed in the above trigger.

  1. The trigger has been written with a SOQL query inside a “for” loop.
  2. There is a DML operation inside a “for” loop.
  3. There is unnecessary data being fetched.

If more than 100 records are passed into the Account object through data loader/API calls, this trigger will easily surpass the governor limit which restricts you from firing more than 100 SOQL queries in a single transaction.

So, How do we optimize/bulkify this trigger. We need to begin by pulling the SOQL query outside the “for” loop so that it doesn’t fire a query every time for each record. Once the SOQL query has been pulled outside, we need to ensure that we put all the records that we want to update into a single list and then fire the update statement on that single list.

So, the modified trigger should look something like below.


trigger accountTestTrggr on Account (after insert, after update) {
   //This queries all Contacts related to the incoming Account records in a single SOQL query
   //This is also an example of how to use child relationships in SOQL
   List<Account> accountsWithContacts = [select id, name, (select id, salutation, descriptio,
   firstname, lastname, email from Contacts)
   from Account where Id IN :Trigger.newMap.keySet()];


   List<Contact> contactsToUpdate = new List<Contact>();

   for(Account a: accountsWithContacts){
      // Use the child relationships dot syntax to access the related Contacts
      for(Contact c: a.Contacts){

         c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;

         contactsToUpdate.add(c);

      }
   }

   //Now outside the FOR Loop, perform a single Update DML statement.
   update contactsToUpdate;
}

 

Well, the above trigger has now got a clean chit from the governor limit. However, we can still go ahead and optimize the data being fetched by this trigger. If you notice, we we fetching the data from the Account object, even though we only intend to update only the Contact object. So, rather that doing that we pull only the Contact object data and do the required processing. Yes, it can be indeed achieved by again modifying the trigger as shown below.


trigger accountTestTrggr on Account (after insert, after update) {
   //This queries all Contacts related to the incoming Account records in a single SOQL query   without fetching Account data.
   List<Contact> Cons = [select id, salutation, description,
   firstname, lastname, email from Contact where AccountId IN :Trigger.newMap.keySet()];

   for(Contact c: Cons){

      c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;

   }
   
   update Cons;
}

That’s the final piece of trigger which should work flawlessly irrespective of the number of records flowing into the Account object.

Please feel free to comment for any clarifications/issues

 

Advertisements