+ Start a Discussion
santoshk.behera1.3878016627537622E12santoshk.behera1.3878016627537622E12 

Invoke a trigger to ensure that the no_of_contacts field is incremented whenever there is a new contact registered to an Account. This trigger has to work after update using Map.Can some one help me?

trigger  countNoOfContact on Contact (before insert,after insert,after update,after delete,after undelete) {
   
 
      Set<ID> setAccId=new Set<ID>();
       Set<ID> oldAccId = new Set<ID>();
   Map<Id,Integer> newContactMapping=new Map<Id,Integer>();
 
    if((trigger.IsInsert)||(trigger.IsUndelete))
    {
        
    for(Contact con1:trigger.new)
    {   
       if(con1.AccountId!=null)
       {
          setAccId.add(con1.AccountId);
           if(newContactMapping.containsKey(con1.AccountId)){
               newContactMapping.put(con1.AccountId,  newContactMapping.get(con1.AccountId)+1);
              
           }
           else
           {
              newContactMapping.put(con1.AccountId, 0);  
           }
          
       }
   
   } 
    }
   
    else if((trigger.IsDelete)){
       
      for(Contact con1:trigger.old)
    {   
       if(con1.AccountId!=null)
       {
          setAccId.add(con1.AccountId);
           if(newContactMapping.containsKey(con1.AccountId)){
               newContactMapping.put(con1.AccountId,  newContactMapping.get(con1.AccountId)+1);
              
           }
           else
           {
              newContactMapping.put(con1.AccountId, 0);  
           }
          
       }
   
   }   
       
   
    }
   
    else if(trigger.IsUpdate)
       
    {
      
        for(Contact con1:trigger.new)
        {
          
            if(con1.AccountId!=trigger.oldMap.get(con1.id).AccountId)
            setAccId.add(con1.AccountId);
            oldAccId.add(trigger.oldMap.get(con1.id).AccountId);
            if(con1.AccountId!=null)  
               
            {     
               
                if(newContactMapping.containsKey(con1.AccountId)){
               newContactMapping.put(con1.AccountId,  newContactMapping.get(con1.AccountId)+1);
              
           }
           else
           {
              newContactMapping.put(con1.AccountId, 0);  
           }
            }
           
           
        }
       
    
    }
           
       
       
       Map<ID,Account> mapToAccount=new Map<ID,Account>([select id,No_Of_Contacts__c,(select id from Contacts )from Account where id in:setAccId ]);
       Map<ID,Account> mapToAccount1=new Map<ID,Account>([select id,No_Of_Contacts__c,(select id from Contacts)from Account where id in:oldAccId ]);
   
   
   
                for(ID accId:mapToAccount.keySet())
                {
                    Account acc=mapToAccount.get(accId);
                    if(trigger.IsInsert){
                    acc.No_Of_Contacts__c=acc.Contacts.size()+ newContactMapping.get(accId);
                    }
                    else{
                         acc.No_Of_Contacts__c=acc.Contacts.size()- newContactMapping.get(accId);
                    }
                }      
                   
                   
                 update mapToAccount.values();

             for(ID accId:mapToAccount1.keySet())
                {
                    Account acc=mapToAccount1.get(accId);
                    if(trigger.IsInsert){
                    acc.No_Of_Contacts__c=acc.Contacts.size()+ newContactMapping.get(accId);
                    }
                    else{
                         acc.No_Of_Contacts__c=acc.Contacts.size()- newContactMapping.get(accId);
                    }
                }      
                   
                   
                 update mapToAccount1.values();   
                       
}
MJ Kahn / OpFocusMJ Kahn / OpFocus
You've got a lot going on in your code, including both Before and After tirggers, which may mean that you're double-counting Contacts.

 In general, when you write this much code, it's very helpful to include comments describing the altorithm you're using. Adding those coments can actually help you find the logic errors.

All that notwithstanding, you might consider a different approach. When I have to write a trigger that essentially performs a roll-up summary, I tend to implement it as two triggers. First, I create a checkbox field on the parent object, and then I write a Before trigger on the parent object that says, "Whenever the checkbox becomes checked, use an aggregate query to calculate and store the number of child records for the parent, and also uncheck the checkbox (so it can be checked again in the future)." Then I write a trigger on the child object that says, "Whenever a record is touched (i.e., inserted, deleted, undeleted, or updated with a change to its parent relationship), check the checkbox on the parent record (for updates, check the checkbox for both the old and the new parent records)." Checking the checkbox causes the parent record to re-calculate its counts. 

By letting the parent record maintain its own count, I'm simplifying the trigger logic that does the math, and because the child object's trigger only has to worry about checking a checkbox, its logic is simpler too. Also, because we're completely re-calculating the count, rather than adding or subtracting from it, we don't have to worry that, if the count somehow becomes wrong, it'll stay wrong. And finally, this approach makes it easier for you to calculate the value for existing parent records -- all you have to do is check their checkbox in a simple data load, and they'll calculate their own initial counts. 

The only time this approach isn't a good idea is if you have very large numbers of child records. In that case, you could hit governor limits. That's not usually a problem when you're working with Accounts and Contacts, though.

One little nit - if the Before trigger on the parent object uses an aggregate query to calculate the count of child records, remember that, if a given parent record has NO child records (because the last one was just deleted), the aggregate query won't return 0 - it just won't return a result for that parent record. So before you process the results of the aggregate query, loop over all of the Accounts where the checkbox was checked and set the count for each one to zero. Then process the results of the aggregate query, overwriting the zeros with the calculated values. 

If this doesn't make sense, let me know and I'll provide some code, but you'll learn more if you can write it yourself. 
Subramani_SFDCSubramani_SFDC
ur code is working fine..u will use after insert and after update to count the contacts....

trigger  countNoOfContact on Contact (after insert,after update,after delete,after undelete) {
  

      Set<ID> setAccId=new Set<ID>();
       Set<ID> oldAccId = new Set<ID>();
   Map<Id,Integer> newContactMapping=new Map<Id,Integer>();

    if((trigger.IsInsert)||(trigger.IsUndelete))
    {
       
    for(Contact con1:trigger.new)
    {  
       if(con1.AccountId!=null)
       {
          setAccId.add(con1.AccountId);
           if(newContactMapping.containsKey(con1.AccountId)){
               newContactMapping.put(con1.AccountId,  newContactMapping.get(con1.AccountId)+1);
             
           }
           else
           {
              newContactMapping.put(con1.AccountId, 0); 
           }
         
       }
  
   }
    }
  
    else if((trigger.IsDelete)){
      
      for(Contact con1:trigger.old)
    {  
       if(con1.AccountId!=null)
       {
          setAccId.add(con1.AccountId);
           if(newContactMapping.containsKey(con1.AccountId)){
               newContactMapping.put(con1.AccountId,  newContactMapping.get(con1.AccountId)+1);
             
           }
           else
           {
              newContactMapping.put(con1.AccountId, 0); 
           }
         
       }
  
   }  
      
  
    }
  
    else if(trigger.IsUpdate)
      
    {
     
        for(Contact con1:trigger.new)
        {
         
            if(con1.AccountId!=trigger.oldMap.get(con1.id).AccountId)
            setAccId.add(con1.AccountId);
            oldAccId.add(trigger.oldMap.get(con1.id).AccountId);
            if(con1.AccountId!=null) 
              
            {    
              
                if(newContactMapping.containsKey(con1.AccountId)){
               newContactMapping.put(con1.AccountId,  newContactMapping.get(con1.AccountId)+1);
             
           }
           else
           {
              newContactMapping.put(con1.AccountId, 0); 
           }
            }
          
          
        }
      
   
    }
          
      
      
       Map<ID,Account> mapToAccount=new Map<ID,Account>([select id,No_Of_Contacts__c,(select id from Contacts )from Account where id in:setAccId ]);
       Map<ID,Account> mapToAccount1=new Map<ID,Account>([select id,No_Of_Contacts__c,(select id from Contacts)from Account where id in:oldAccId ]);
  
  
  
                for(ID accId:mapToAccount.keySet())
                {
                    Account acc=mapToAccount.get(accId);
                    if(trigger.IsInsert){
                    acc.No_Of_Contacts__c=acc.Contacts.size()+ newContactMapping.get(accId);
                    }
                    else{
                         acc.No_Of_Contacts__c=acc.Contacts.size()- newContactMapping.get(accId);
                    }
                }     
                  
                  
                 update mapToAccount.values();

             for(ID accId:mapToAccount1.keySet())
                {
                    Account acc=mapToAccount1.get(accId);
                    if(trigger.IsInsert){
                    acc.No_Of_Contacts__c=acc.Contacts.size()+ newContactMapping.get(accId);
                    }
                    else{
                         acc.No_Of_Contacts__c=acc.Contacts.size()- newContactMapping.get(accId);
                    }
                }     
                  
                  
                 update mapToAccount1.values();  
                      
}
J SJ S
Hi Subramani,

How can I achieve same requirement on self lookup custom object(same object eg. abc__c). relationship field is abc__r.