Saturday, 21 May 2011

Writing Apex Trigger : Save Limits in Trigger

Hi All,

If we do not write triggers in such a way where salesforce limits such as SOQL, dml statements etc are not optimized then you will always see errors like Too Many SOQL etc. Mainly that happens when we have any SOQL or dml in for loop(iteration). But there are cases where we do not use SOQL in loop still we face such issue. Following are the such cases
1) Bulk upload
2) When we have bigger triggers that have more then one SOQL and dml statements, again it might be posible that dml in one trigger invoke another trigger and again that trigger invoke some other.
3)Some time we have custom pages where we save more then one object in a single instance in that case triggers of all the object are also invoked in single instance.

So by this we know that optimizing a trigger to save limits is essential. Now To save limits we should always follows some basic practice :
In a trigger
1) No SOQL or dml in a loop

In this example SOQL and dml both are in loop so when ever this trigger will be fired with bulk of records then Limits will exceed.

trigger testReqTrigger on Account (after update) 
{
    for(Account acc : Trigger.New){
    List<Contact> listCon = [Select Fax from Contact where AccountId =: acc.id];        for(Contact con : listCon){
        con.Fax = acc.Fax;
        }
    update listCon;

    }

Solution :
trigger testReqTrigger on Account (after update) 
    {
        //Get all the contacts for all the accounts in one SOQL
        List< Contact > listCon = [Select Fax , AccountID from Contact where AccountId in: Trigger.New];
        system.debug('****** listContacts  :  '+ listCon ); 
        //Map with Key : Account Id and Value : List of contacts for that account 
        Map< ID , List< Contact > > map_AccID_ContactRecord = new Map< ID , List< Contact > >();
        List< Contact > tempListCon = new List< Contact >();
        //Fill the map
        for(Contact c : listCon)
            {
                if(map_AccID_ContactRecord.containsKey(c.AccountID)) 
                    {
                     
                        tempListCon = map_AccID_ContactRecord.get(c.AccountID);
                        tempListCon.add(c);
                        map_AccID_ContactRecord.put(c.AccountID , tempListCon); 
                    }
                else 
                    {
                        tempListCon = new List< Contact >();
                        tempListCon.add(c);
                        map_AccID_ContactRecord.put(c.AccountID , tempListCon);
                    }
            }
        //List of contacts that will be updated
        List< Contact > listConTBU = new List< Contact >();
        for(Account acc : Trigger.New)
            {
                //check account has list of contacts in map
                if(map_AccID_ContactRecord.containsKey(acc.id)) 
                    {
                       //Loop over the list of contacts
                       for(Contact con : map_AccID_ContactRecord.get(acc.id))
                          {
                              con.Fax = acc.Fax;
                              //Add contact that needed to be updated
                              listConTBU.add(con); 
                          }
                    }
            }
        
        //update list of all the contacts for all accounts
        update listConTBU;

    }


2)Only one SOQL and dml should be there for an object type
    Ex : We have to write a trigger for
           1) Contact Fax is not null  then copy account fax to contact fax field
           2) Contact Phone is not null then copy account phone to contact phone field
trigger testReqTrigger on Account (after update)
    {
        for(Account acc : Trigger.New)
            {
                List<Contact> listConForFaxUpdate = [Select Fax from Contact where Fax != null And AccountId =: acc.id];       
                for(Contact con : listConForFaxUpdate)
                    {
                         con.Fax = acc.Fax;
                    }
                   
                update listConForFaxUpdate;
               
                List<Contact> listConForPhoneUpdate = [Select Phone from Contact where Phone != null And AccountId =: acc.id];       
                for(Contact con : listConForPhoneUpdate )
                    {
                         con.Phone = acc.Phone;
                    }
                   
                update listConForPhoneUpdate;    
            }
          
    }
In above example Contact Object Type has two SOQL and dml, that is worng way of writing trigger

Solution : Query both fields in single SOQL

trigger testReqTrigger on Account (after update) 
    {
        for(Account acc : Trigger.New)
            {
                // Query for both Fax and Phone
                // List contains contact records which do not have both Fax or Phone as null
                List<Contact> listConForUpdate = [Select Fax , Phone from Contact where (Fax != null  OR Phone != null ) And AccountId =: acc.id];        
                for(Contact con : listConForUpdate)
                    {
                         if(con.Fax != null)
                             {
                                 con.Fax = acc.Fax;
                                 if(con.Phone != null)
                                     con.Phone = acc.Phone;
                             }
                         else
                             {
                                 con.Phone = acc.Phone;
                             }    
                    }
                update listConForUpdate;
            }
           
    }

In above example we have used SOQL in a loop that can be removed with our first problems solution.

So these two are the basic practice that we should do so triggers work appropriately with bulk data also. Now after covering this basic practice we will discuss some problems of complex triggers, cyclic triggers where one trigger invokes another and there solutions and order of execution of the trigger.

Regards

2 comments:

  1. Your examination ought uk-essay.net/essays to be organised with the intention that the move from doing your exploration to composing your exposition is straightforward.

    ReplyDelete

Tweet