+ Start a Discussion
ilewi121ilewi121 

How to prevent "Apex CPU Time Limit Exceeded"?


I am building a trigger to automatically associate a custom Account child object with a custom Opportunity child object. My trigger works fine and tests perfectly, except when I went to upload a bunch of records to the new object, I received the following error on every item:

System.LimitException: Apex CPU time limit exceeded

I found a helpful article here:
http://salesforce.stackexchange.com/questions/22223/how-to-code-more-efficient-to-avoid-apex-cpu-time-limit-exceeded

But I'm still not sure what I'm doing wrong. Does anyone have any ideas?

Here is the trigger code:

<pre>
trigger associateOppChildtoAccChild on OppChild__c (before insert, before update) {

    Set<Id> OppSet = new Set<Id>();
    for(OppChild__c OppChild :Trigger.new){
        OppSet.add(OppChild.Opportunity__c);
    }
   
    Map<ID,ID> OPP_ACC = new Map<ID,ID>();
    Set<Id> AccSet = new Set<Id>();
    for(Opportunity OPP:[SELECT Id, AccountId from Opportunity where Id in :OppSet]){
        OPP_ACC.put(OPP.Id, OPP.AccountId);
        AccSet.add(OPP.AccountId);       
    }

    Map<ID,ID> ACC_OppChild = new Map<ID,ID>();
    for(OppChild__c OppChild :Trigger.new){
        for(ID OPP :OPP_ACC.keyset()){
            if(OPP == OppChild.Opportunity__c){
                ACC_OppChild.put(OPP_ACC.get(OPP), OppChild.Id);
            }
        }
    }
   
    List<AccChild__c> AccChildList = [SELECT Id, Account__c, Type__c, Annual_Spend__c, Number_of_Shipment_Transactions__c FROM AccChild__c WHERE Account__c in :AccSet];
   
    for(OppChild__c OppChild :Trigger.new){
        for(AccChild__c m: AccChildList){
            if(m.Type__c == OppChild.AccChild_Type__c){
                for(ID ACC :ACC_OppChild.keyset()){
                    if(ACC == m.Account__c){
                        OppChild.AccChild__c = m.Id;
                        System.debug('OppChild AccChild = ' + OppChild.AccChild__c);
                        OppChild.Annual_Spend__c = m.Annual_Spend__c;
                        OppChild.Number_of_Shipment_Transactions__c = m.Number_of_Shipment_Transactions__c;
                    } else {
                        OppChild.adderror(OppChild.AccChild_Type__c + ' AccChild must exist on Account to add OppChild');
                    }
                }
            }
        }
    }  
}
</pre>
Best Answer chosen by ilewi121
ilewi121ilewi121
It looks like I overthought this one: All I had to do was add a formula field to the Opportunity child object that pulled the Account Id, bypassing the need for all the maps.

<pre>trigger associateOppChildtoAccChild on OppChild__c (before insert, before update) {

    Set<Id> AccSet = new Set<Id>();
    for(OppChild__c OppChild :Trigger.new){
        AccSet.add(OppChild.AccountId__c);
    }

    List<AccChild__c> AccChildList = [SELECT Id, Account__c, Type__c, Annual_Spend__c, Number_of_Shipment_Transactions__c FROM AccChild__c WHERE Account__c in :AccSet ];
 
    for(OppChild__c OppChild :Trigger.new){
        for(AccChild__c m: AccChildList){
            if(m.Type__c == OppChild.AccChild_Type__c && m.Account__c == OppChild.AccountId__c){
                OppChild.AccChild__c = m.Id;
                System.debug('OppChild AccChild = ' + OppChild.AccChild__c);
                OppChild.Annual_Spend__c = m.Annual_Spend__c;
                OppChild.Number_of_Shipment_Transactions__c = m.Number_of_Shipment_Transactions__c;
            }
        }
    }
}</pre>

For those who would like more information on using maps, though, I did find some good articles:
http://salesforce.stackexchange.com/questions/22223/how-to-code-more-efficient-to-avoid-apex-cpu-time-limit-exceeded
http://stackoverflow.com/questions/9521740/apex-map-within-a-map
http://www.x2od.com/2012/07/25/populate-map-without-fo-loops
https://developer.salesforce.com/forums?id=906F00000008yc1IAA

All Answers

Vinita_SFDCVinita_SFDC
Hello,

Please refer below blog to understand and overcome this limit:

http://cloudyworlds.blogspot.in/2013/10/battling-cpu-time-out-limit-in-apex-sfdc.html
ilewi121ilewi121
Thank you, Vinita_SFDC, that was a helpful article. It seems I have used too many loops for the CPU limit.

Does anyone know how I could optimize this code and remove some of the loops?

I'm thinking maybe I could put a map inside a map, but how would I do that? This article has some ideas, but I'm having trouble applying it to my scenario: 
http://stackoverflow.com/questions/9521740/apex-map-within-a-map
ilewi121ilewi121
It looks like I overthought this one: All I had to do was add a formula field to the Opportunity child object that pulled the Account Id, bypassing the need for all the maps.

<pre>trigger associateOppChildtoAccChild on OppChild__c (before insert, before update) {

    Set<Id> AccSet = new Set<Id>();
    for(OppChild__c OppChild :Trigger.new){
        AccSet.add(OppChild.AccountId__c);
    }

    List<AccChild__c> AccChildList = [SELECT Id, Account__c, Type__c, Annual_Spend__c, Number_of_Shipment_Transactions__c FROM AccChild__c WHERE Account__c in :AccSet ];
 
    for(OppChild__c OppChild :Trigger.new){
        for(AccChild__c m: AccChildList){
            if(m.Type__c == OppChild.AccChild_Type__c && m.Account__c == OppChild.AccountId__c){
                OppChild.AccChild__c = m.Id;
                System.debug('OppChild AccChild = ' + OppChild.AccChild__c);
                OppChild.Annual_Spend__c = m.Annual_Spend__c;
                OppChild.Number_of_Shipment_Transactions__c = m.Number_of_Shipment_Transactions__c;
            }
        }
    }
}</pre>

For those who would like more information on using maps, though, I did find some good articles:
http://salesforce.stackexchange.com/questions/22223/how-to-code-more-efficient-to-avoid-apex-cpu-time-limit-exceeded
http://stackoverflow.com/questions/9521740/apex-map-within-a-map
http://www.x2od.com/2012/07/25/populate-map-without-fo-loops
https://developer.salesforce.com/forums?id=906F00000008yc1IAA
This was selected as the best answer
Vinothini Murugesh 23Vinothini Murugesh 23
Getting CPU time limit exceeded error. This is my trigger. 

trigger UpdateRelIntConIds5 on Contract_Group_Association__c (after delete, after insert, after update) {
    Set<Id> accounts = new Set<Id>();

    Set<Id> cons = new Set<Id>();

    if (Trigger.old != null) {
        system.debug('inside update');
        for (Contract_Group_Association__c c : Trigger.old) {
            if (c.Internal_Contract_Group__c != null)
                cons.add(c.Internal_Contract_Group__c);
        }
    }


    if (Trigger.new != null) {
        system.debug('inside insert');
        for (Contract_Group_Association__c c : Trigger.new) {
            if (c.Internal_Contract_Group__c != null)
                cons.add(c.Internal_Contract_Group__c);
        }
    }

    List<Customer_Groups_Association__c> conGroup = [select Internal_Contact_Group__c from Customer_Groups_Association__c where Internal_Contract_Group__c in :cons];
system.debug('conGroup'+conGroup.size());
    for (Customer_Groups_Association__c cx : conGroup) {
        accounts.add(cx.Internal_Contact_Group__c);
          system.debug('cx.Internal_Contact_Group__c: '+cx.Internal_Contact_Group__c);
    }   
   system.debug('Groups'+accounts);
    UpdateRelatedInternalContractIds ur = new UpdateRelatedInternalContractIds(accounts);
    ur.go();
}

Related apex class :

global class UpdateRelatedInternalContractIds {
    public Set<Id> accounts {get; set;}

    public UpdateRelatedInternalContractIds(Set<Id> accounts) {
        this.accounts = accounts;
    }

    public void go() {
        Set<Id> contractGroups = new Set<Id>();

        Map<String, Set<String>> customers = new Map<String, Set<String>>();

        if (accounts == null)
            return;

        List<Customer_Groups_Association__c> clist = [select id, Internal_Contract_Group__c, Internal_Contact_Group__c from Customer_Groups_Association__c where Internal_Contact_Group__c in :accounts];

        if (clist != null) {
            for (Customer_Groups_Association__c c : clist) {
                contractGroups.add(c.Internal_Contract_Group__c);

                Set<String> t = customers.get(c.Internal_Contact_Group__c);

                if (t == null) {
                    t = new Set<String>();
                    customers.put(c.Internal_Contact_Group__c, t);
                }

                t.add(c.Internal_Contract_Group__c);
            }
        }

        Map<String, Set<String>> contractIds = new Map<String, Set<String>>();

        List<Contract_Group_Association__c> dlist = [select id, Internal_Contract__r.Internal_Contract_ID__c, Internal_Contract_Group__c from Contract_Group_Association__c where Internal_Contract_Group__c in :contractGroups];

        if (dlist != null) {
            for (Contract_Group_Association__c d : dlist) {
                Set<String> t = contractIds.get(d.Internal_Contract_Group__c);

                if (t == null) {
                    t = new Set<String>();
                    contractIds.put(d.Internal_Contract_Group__c, t);
                }

                t.add(d.Internal_Contract__r.Internal_Contract_ID__c);
            }
        }

        List<Account> customerList = [select id, Related_Internal_Contract_IDs__c from Account where id in :accounts];

        for (Account c : customerList) {
            String result = '';

            if (customers.containsKey(c.id)) {
                system.debug('customer@@' + c);
                for (String s : customers.get(c.id)) {
                    if (!contractIds.containsKey(s))
                        continue;

                    for (String x : contractIds.get(s)) {
                        if (result != '')
                            result += ',';

                        result += x;    
                    }
                }
            }

            c.Related_Internal_Contract_IDs__c = result;
        }

        update customerList;
    }
}

 
Vinothini Murugesh 23Vinothini Murugesh 23
Any one please help to solve the above code