0

I wrote one trigger which will count the number of child records and update it to parent object field.

If I create 2 records then count is giving 4 instead of 2... Please help me to overcome the issue.

trigger HobbyTrigger on hed__Relationship__c  (after insert,after Update,after delete) {
map<Id, Integer> mapEmpIdHobbyCount = new map<Id, Integer>();
if(Trigger.isAfter){
if((trigger.isInsert || trigger.isUpdate)) {
    for(hed__Relationship__c  hob : trigger.new) {
        if(hob.hed__Contact__c != null && (hob.hed__Type__c=='Son' || hob.hed__Type__c=='Daughter' || hob.hed__Type__c=='Husband' || hob.hed__Type__c=='Wife' || hob.hed__Type__c=='Partner')) {
            if(!mapEmpIdHobbyCount.containsKey(hob.hed__Contact__c)) {
                mapEmpIdHobbyCount.put(hob.hed__Contact__c, 1);
            } else {
                mapEmpIdHobbyCount.put(hob.hed__Contact__c, mapEmpIdHobbyCount.get(hob.hed__Contact__c) + 1);
            }
        }
    }
} else if( Trigger.isDelete && Trigger.isAfter ) {
    for(hed__Relationship__c  hob : trigger.old) {
        if(hob.hed__Contact__c != null && (hob.hed__Type__c=='Son' || hob.hed__Type__c=='Daughter' || hob.hed__Type__c=='Husband' || hob.hed__Type__c=='Wife' || hob.hed__Type__c=='Partner')) {
            if(!mapEmpIdHobbyCount.containsKey(hob.hed__Contact__c)) {
                mapEmpIdHobbyCount.put(hob.hed__Contact__c, -1);
            } else {
                mapEmpIdHobbyCount.put(hob.hed__Contact__c, mapEmpIdHobbyCount.get(hob.hed__Contact__c) - 1);
            }
        }
    }
}
}
if(mapEmpIdHobbyCount.size() > 0) {
    List<Contact> listEmp = [SELECT Id, Number_Of_Dependents__c FROM Contact WHERE Id IN : mapEmpIdHobbyCount.keySet()];

    for(Contact emp : listEmp) {
        emp.Number_Of_Dependents__c += mapEmpIdHobbyCount.get(emp.Id);
    }

    update listEmp;
}
}
  • It may be possible that first time when record is inserted ...it's calculated...and again same record is being updated in same transaction ( by workflow, process builder, apex)...casuing to run after update and doubles count. – Ayub Jan 2 at 13:21
3

Odds are something in your org (another trigger? a workflow? a process?) is causing the trigger to be fired again during the update event. This is a classic trigger recursion problem.

Fortunately, the most stable recursion defense looks like it'd work for you here.

First, break all of your logic out into a handler class.

Then, in the handler, create a new static variable:

static Set<Id> alreadyProcessedRelationships = new Set<Id>();

Each time you count a relationship into mapEmpIdHobbyCount on your update and insert events, first check whether you've already seen that specific record this transaction, and skip it if so.

if (alreadyProcessedRelationships.contains(hob.Id)) continue;

Then, when you do add it to mapEmpIdHobbyCount, also add its Id to alreadyProcessedRelationships. This will ensure that your trigger processes each relationship exactly once per transaction in insert/update events.

Alternate Solution

Just use Declarative Lookup Rollup Summaries. It implements all the logic for you, so you don't need to write a line of code, and it's free and open source.

1

Another approach you could take is to ensure that your logic here is idempotent (i.e. if you give the same input, you get the same output).

The easy/naive approach to incrementing child record counts (find the delta, just add straight to current value of record) is not idempotent.

The easy/naive fix for this is to re-count all of your child records. You could also use a query to do the counting for you.

The pseudocode for that would look like

trigger{

    listOfRecords = trigger.new or trigger.old depending on the trigger event
    listOfParents = new list

    // Iterate over the trigger records to extract the parent Ids that we need to re-count
    for(rec :listOfRecords){
        listOfParents.add(rec.parentId);
    }

    // Method 1: Aggregate query, grouped by parent ids
    // fetching the result would look like (Integer)aggResult.get('numChildren');
    [SELECT ParentId, COUNT(Id) children FROM Child WHERE ParentId IN :listOfParents GROUP BY ParentId]
    // Method 2: Parent-Child subquery
    // fetching the result would look like parent.Children__r.size()
    [SELECT Id, (SELECT Id FROM Children__r) FROM Parent WHERE Id IN :listOfParents]

    // Iterate over query results, and prepare parents for updating
    for(result :queryResults){
        // If you have the parent Id, you can specify that in the SObject constructor
        //   to avoid needing to explicitly query the record.
        // If we can avoid a query, it's almost always worth avoiding.
        parentsToUpdate.add(new Parent(
            Id = parentId,
            numChildren = resultFromQuery
        );
    }

    update parents;
}

This approach is very similar to the one taken by Declarative Lookup Rollup Summaries (which I would recommend considering).

Both of the query methods have their pros and cons:

  • Method 1
    • Probably safer
    • A Null check on the "children" field alias is probably a good idea (null + null = null in this method)
    • If a parent has no children, it won't appear in the results for this query
  • Method 2
    • Probably more intuitive to work with
    • You need to know and use the "Child Relationship Name"
    • You get a List<SObject>, which means no need for a null check, and all parents are present
    • Can require a nested loop instead of just evaluating parent.children__r.size() if there are enough child records
0

Have you checked if your trigger is being called recursively? If the trigger is being called more than once in a single transaction, I can update the number of child records times n, where n is the number of times the trigger is called.

Look up blog posts on how to prevent trigger recursion.

  • Hi pgonzaleznetwork, thanks for the quick reply, on deletion its working perfectly, but only on creation I am getting the issue. – user61203 Jan 2 at 13:06
  • 1
    That's because delete triggers cannot be called recursively, but insert/update triggers can. This video has an example that is very similar to what you are experiencing: youtube.com/watch?v=amOhAqUaYoo – CommonCoreTawan Jan 2 at 13:08
0

Why not use Aggregate Querry here? It would be faster and you dont have to think much about recursion , as it counts child from the database.

trigger HobbyTrigger on hed__Relationship__c  (after insert,after Update,after delete) {
    Set<Id> contactISet = new Set<Id>();
    if(Trigger.isAfter){
        //To Iterate over
        List<hed__Relationship__c> toIterateOverHedR = (Trigger.isInsert || trigger.isUpdate)?Trigger.new : Trigger.old;
        for(hed__Relationship__c hedr : toIterateOverHedR){
            contactISet.add(hedr.hed__Contact__c);
        }
        List<Contact> listofContactTobeUpdated = new List<Contact>();
            //Use Aggregate Querry
        AggregateResult groupedResults[] = [SELECT hed__Contact__c, Count() cnt
                                            FROM hed__Relationship__c
                                            WHERE ((hed__Type__c = 'Son' OR  hed__Type__c ='Daughter' OR hed__Type__c = 'Husband' OR hed__Type__c = 'Wife' OR hed__Type__c = 'Partner') AND (hed__Contact__c IN :contactISet ))
                                            GROUP BY hed__Contact__c ] ;

        for(AggregateResult groupedResult : groupedResults){
            Contact con = new Contact(groupedResult.get('hed__Contact__c') ,Number_Of_Dependents__c =groupedResult.get('cnt')  );
            listofContactTobeUpdated.add(con);
        }

        update listofContactTobeUpdated


    }

}

Src : https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_SOQL_agg_fns.htm

Also as David Mentioned: The best solution is the one that doesnt require you to code, So I would recommend Declarative Lookup Rollup Summaries

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy