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
Art SmorodinArt Smorodin 

Apex governor limit warning/Debug logs

Hi, 

I have been recieving an Apex governor limit warning (Number of SOQL queries: 97 out of 100) after making an update on an Opportunity and decided to look into reasons behind it. Now I am confused and need help. 

Here is the deal. We have 3 or 3 different triggers on an Opportunity object, but for now lets concentrate on one. Trigger name is "Update_Split_Quota" and it is after update type trigger. Here is the code (I know it is not idel, it is still work in proggress):
trigger Update_Split_Quota on Opportunity (After Update) {
     
     Opportunity[] OppIdNew = Trigger.new;
     Opportunity[] OppIdOld = Trigger.old;
     if (Trigger.isAfter){
         List<Opportunity> olis = [SELECT Id,AccountId FROM Opportunity WHERE Id IN: Trigger.newMap.keySet()];
      for(Opportunity opp: olis){
          List<OpportunitySplit> oppsplit = [SELECT Id, OpportunityId, SplitOwnerId, Sales_Quota__c, Legal_Accepted_Date__c FROM OpportunitySplit WHERE OpportunityId = :opp.id];
            Account[] account = [Select OwnerId FROM Account Where ID = :opp.AccountID];
            if(OppIdNew[0].Order_Type__c=='Services Only'&& OppIdNew[0].StageName == 'Closed Won'){
                opp.OwnerId = account[0].OwnerId;
                //update opp;
            }          
          for (OpportunitySplit os:oppsplit) {
              if(os.Legal_Accepted_Date__c != null) { //Only run the trigger if legal accepted
                  date Month_Start = os.Legal_Accepted_Date__c.toStartOfMonth();
              
                  //date Month_End = Month_Start.addMonths(1);
                  List<Sales_Quota__c> sales = [SELECT Id, User__C,Month__c, Quarter__c FROM Sales_Quota__c WHERE (User__C = :os.SplitOwnerId) AND (Month__c=:Month_Start) LIMIT 1];//(Quarter__c = THIS_YEAR) AND (User__C = :oppsplit.SplitOwner.id)
                  
                  if(sales.size()!=0) { //for users who do not have quotas
                      Sales_Quota__c s = sales.get(0);
                      os.Sales_Quota__c=s.ID;//Sales_Quota__c = s.ID;
                      update oppsplit;
                  }
              }
          }
      }
      }
}
But when I do an update on an Opportunity it does exactly what I want it to do, and I immidiatly get an email with governor limit warning. So I decided to run a debug log to see what is going on, and this is the confusing part. In the log I see that this trigger is being called 6 different time. Each time it is called 3 Select statements inside it are run, and it add up to a lot (18 out 100 possible). My question is WHY DOES IT GET CALLED 6 DIFFERENT TIMES if I only update a single Opportunity (I update an existing Opp, not creating a new one. I simply switch the stage to "Closed Won").

Attached is a small snapshot of the Debug log file showing how my trigger is called and the number of time it is being called

Debug log snapshot
 
Best Answer chosen by Art Smorodin
Art SmorodinArt Smorodin
HI Keshab, 

Yes there are about dozen workflows on the Opportunity object, and I would say about half of them are field updates.
If this is the case, is there any way around it? 

Thanks.

All Answers

Keshab AcharyaKeshab Acharya
Is there any workflow field update associated? This will make your trigger run again and again.
Art SmorodinArt Smorodin
HI Keshab, 

Yes there are about dozen workflows on the Opportunity object, and I would say about half of them are field updates.
If this is the case, is there any way around it? 

Thanks.
This was selected as the best answer
Keshab AcharyaKeshab Acharya
Here are some best practices you can follow.
  • Never write multiple trigger for same Object. Solution : Write one trigger but multple helper classes which will be called from the single trigger for various funtionality. This is because if we have multiple trigger, we will not have control over the sequence of triggers.
  • Avoid writing business logic in Trigger itself. Solution : The business login should be written inside helper class.
You can find more about Trigger Order of Execution at https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_order_of_execution.htm

btw your 97 out of 100 is for entire transaction?
Art SmorodinArt Smorodin
Yes it was that one update on that one Opportunity. I got an email saying "Number of SOQL queries: 97 out of 100", and after I downloaded debug log (13K lines of logs) last lines were 
14:45:41.717 (5717252355)|SYSTEM_METHOD_EXIT|[108]|System.debug(ANY)
14:45:42.077 (5717265758)|CUMULATIVE_LIMIT_USAGE
14:45:42.077|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 97 out of 100 ******* CLOSE TO LIMIT
  Number of query rows: 1986 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 22 out of 150
  Number of DML rows: 32 out of 10000
  Maximum CPU time: 1095 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

14:45:42.077|CUMULATIVE_LIMIT_USAGE_END

14:45:41.717 (5717310265)|CODE_UNIT_FINISHED|OpportunityTrigger on Opportunity trigger event AfterUpdate for [006U000000Ke0tF]
14:45:41.718 (5718361561)|WF_ACTIONS_END| Field Update: 5;
14:45:41.718 (5718367903)|CODE_UNIT_FINISHED|Workflow:Opportunity
14:45:42.098 (6098675389)|CODE_UNIT_FINISHED|TRIGGERS
14:45:42.098 (6098693352)|EXECUTION_FINISHED