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
Ashley Cobb 24Ashley Cobb 24 

System.LimitException: Too many SOQL queries: 101 Stack Trace: Class.RollUpSummaryUtility.rollUpTrigger: line 83, column 1

Hello!

I am trying to upload new code, and am coming up with the error "System.LimitException: Too many SOQL queries: 101 
Stack Trace: Class.RollUpSummaryUtility.rollUpTrigger: line 83, column 1".  I am a very new Developer, but do not understand why I am getting the error.  It seems to be the bolded area. Any help would be appreciated!!


public class RollUpSummaryUtility {
   

    public class fieldDefinition {
        public String operation {get;set;}
        public String childField {get;set;}
        public String parentField {get;set;}
         
        public fieldDefinition (String o, String c, String p) {
            operation = o;
            childField = c;
            parentField = p;
        }
    }
     
    public static void rollUpTrigger(list<fieldDefinition> fieldDefinitions, list<sObject> records, String childObject, String childParentLookupField, String parentObject, String queryFilter) {
         
        //Limit the size of list by using Sets which do not contain duplicate elements prevents hitting governor limits.
        set<Id> parentIds = new set<Id>();
         
        for(sObject s : records) {
            parentIds.add((Id)s.get(childParentLookupField));
        }
         
        //Populate query text strings to be used in child aggregrator and parent value assignment.
        String fieldsToAggregate = '';
        String parentFields = '';
         
        for(fieldDefinition d : fieldDefinitions) {
            fieldsToAggregate += d.operation + '(' + d.childField + ') ' + ', ';
            parentFields += d.parentField + ', ';
        }
         
        //Using dynamic SOQL with aggergate results to populate parentValueMap.
        String aggregateQuery = 'Select ' + fieldsToAggregate + 
        childParentLookupField + ' from ' + childObject + ' where  ' + 
        childParentLookupField + ' IN :parentIds ' + queryFilter + ' ' +
        ' group by ' + childParentLookupField;
         
        //Map will contain one parent record Id per one aggregate object.
        map<Id, AggregateResult> parentValueMap = new map <Id, AggregateResult>();
         
        for(AggregateResult q : Database.query(aggregateQuery)){
            parentValueMap.put((Id)q.get(childParentLookupField), q);
        }
         
        //List of parent object records to update.
        list<sObject> parentsToUpdate = new list<sObject>();
         
        String parentQuery = 'select ' + parentFields + ' Id ' + ' from ' + parentObject + ' where Id IN :parentIds';
         
        //For each affected parent object, retrieve aggregate results and for each field definition add aggregate value to parent field.
        for(sObject s : Database.query(parentQuery)) {
             
            Integer row = 0; //row counter reset for every parent record
            
            for(fieldDefinition d : fieldDefinitions) {
                String field = 'expr' + row.format();
                AggregateResult r = parentValueMap.get(s.Id);
                //r will be null if no records exist (e.g. last record deleted)
                if(r != null) { 
                    Decimal value = ((Decimal)r.get(field) == null ) ? 0 : 
                        (Decimal)r.get(field);
                    s.put(d.parentField, value);
                } else {
                    s.put(d.parentField, 0);
                }
                row += 1; //plus 1 for every field definition after first
            }
            parentsToUpdate.add(s);
        }
         

        //Ff parent records exist, perform update of all parent records with a single DML statement.
        if(parentsToUpdate.Size() > 0) {
            update parentsToUpdate;
        }
         
    }
 
}
UC InnovationUC Innovation
Is this function called somewhere inside a loop or trigger? Typically this is why someone would get this error and that is why it is a general rule of thumb to never place a query inside a for loop. I cant see you doing that here. This function might be called elsewhere in the code but check out the documentation for more info on Apex governor limits: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm

If you figure out what exactly is calling this function or have any more information on this i would be more than glad to help resolve.

One solution i can think of is to cache the values returned in the parentquery so you dont query the same object. You can achieve this by declaring a static variable for the class. something like 
// cache the values so we dont query something we have already queried for
	public static Map<Id, sObject> parentIdToParentObjectMap = new Map<Id, sObject>();

and store new results. This map will fill up as you query for new records that are not in this map

Hope this helps!

AM
Ankit SehgalAnkit Sehgal

The error might be in the trigger or apex class that is calling this code.

This class looks fine. if you are calling this class with in a trigger make sure it is not getting called recursively.

Ashley Cobb 24Ashley Cobb 24
UC Innovation, 

This is the trigger that is using the code above.

trigger ContractRollUpAssets on Asset (after insert, after update, after delete, after undelete) {
    //Rolls up Assets fields on the related Contract records.
    //=============================================================
    
    if(trigger.isInsert || trigger.isUpdate || trigger.isUnDelete){
        //Create the list of rollups we want.
        list<RollUpSummaryUtility.fieldDefinition> fieldDefinitions = new list<RollUpSummaryUtility.fieldDefinition>{
            new RollUpSummaryUtility.fieldDefinition('COUNT', 'ID', 'Active_Assets__c')
        };
            
        //Call the rollup utility.
        RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.new, 'Asset', 'Contract__c', 'Contract', 'and Status != \'Inactive\' and ZARR__c > 0');
    }
    
    if(trigger.isDelete){
        //Create the list of rollups we want.
        list<RollUpSummaryUtility.fieldDefinition> fieldDefinitions = new list<RollUpSummaryUtility.fieldDefinition>{
            new RollUpSummaryUtility.fieldDefinition('COUNT', 'ID', 'Active_Assets__c')
        };
            
        //Call the rollup utility.
        RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.old, 'Asset', 'Contract__c', 'Contract', 'and Status != \'Inactive\'');
    }
    
}


Is this happening because it is being called in the if statement?

Thanks again!
Ashley
UC InnovationUC Innovation
Are you doing mass insert/update/deletes. If this is the case then it might be due to the trigger being callled too many times. you might want to try using DML statements on lists of records instead of on the individuals.

Although it seems like you might be hitting this due to the trigger on Asset calling this function and updating contacts which also fire some trigger. In this case since triggers are being fired in this manor you might want to restructure your code to avoid triggers firing recursively or update less records at a time. Maybe batch apex might be your solution here.

Hope this helps