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. – Venkata Sainath 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 – CRDev 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

Not the answer you're looking for? Browse other questions tagged or ask your own question.