1

Yesterday I got rejected from a job interview because the interviewer asked me to write a trigger to limit the number of contacts per account and I wrote exactly the below but he said this won't work and it is not the correct way to write the same. I ran the same and it is working fine I am not able to see any problem with the below code can someone please help me understand from the interview's perspective why he said this is not the right way.

    trigger ContactTrigger on Contact (after insert, before Insert, before update, after update) {

                if(Trigger.operationType == triggerOperation.AFTER_INSERT || Trigger.operationType == triggerOperation.AFTER_UPDATE) {
                    set<Id> IdSet =new set<Id>();
                    for(Contact cot : trigger.new) {
                        if(cot.accountID != null) {
                            IdSet.add(cot.accountId);
                        }
                    }

                    Integer contactListCount = [Select count() from contact where accountID IN: IdSet];

                if(contactListCount > 2) {
                    for(contact cop : trigger.new) {
                        cop.addError('cannot have more than 2 contacts per account');
                    }
                }
            }
}

4 Answers 4

3

This trigger is not bulkified. It can't handle more than one account at a time. To fix the problem, an aggregate query would have been the most efficient solution. Here's my version of this trigger:

trigger BlockMoreThan2ContactOnAccount on Contact (after insert, after update, after undelete) {
  Set<Id> accountIds = new Set<Id>();
  for(Contact record: Trigger.new) {
    accountIds.add(record.AccountId);
  }
  accountIds.remove(null);
  Set<Id> morethan2Contacts = new Map<Id, AggregateResult>([
    SELECT AccountId Id
    FROM Contact
    WHERE AccountId = :accountIds
    GROUP BY AccountId
    HAVING COUNT(Id) > 2]).keySet();
  for(Contact record: Trigger.new) {
    if(moreThan2Contacts.contains(record.AccountId)) {
      record.AccountId.addError('You may not have more than 2 contacts per account.');
    }
  }
}

You should always look for opportunities to use aggregate results (such as summing, counting, and finding averages) for performance reasons.

1
  • Thank you so much, I will always keep this in mind
    – gs650x
    Commented Apr 14, 2019 at 16:47
1

This is the best example to use child soql to handle bulky records.. Please see the Below logic..

trigger ContactTrigger on Contact (after insert, before Insert, before update, after update) {
    if ((Trigger.isInsert || Trigger.isUpdate) && Trigger.isAfter) {
        Set<Id> setofAccountId = new Set<Id>();
        for(Contact objCon : trigger.new)
            setofAccountId.add(objCon.accountid)
        //Get all account contact related values
        Map<Id,Account> mapofAccIdToContacts = new Map<Id,Account>([Select Id,(Select Id From Contacts) From Account Where Id IN : setofAccountId]);
        for(Contact objCon : trigger.new){
            if(mapofAccIdToContacts.containskey(objCon.accountID) && mapofAccIdToContacts.get(objCon.accountID).Contacts.size() > 2)
                objCon.addError('cannot have more than 2 contacts per account');
        }
    }
}
2
  • Thank you for responding but what if the account query fetch more than 50K records
    – gs650x
    Commented Apr 14, 2019 at 10:37
  • For that case, you can use where clause in soql to store accountid in the set. I update the answer, Please have a look
    – Sarvesh
    Commented Apr 14, 2019 at 11:20
1

I would recommend creating a developer account to test said code with some debug statements to see what the output is.

Your soql query returns the total number of contacts in each account. This will work if there is only one account in the org, But it won't if there're more.Example: if there are 3 accounts with 4 contacts each, the output of contactListCount will be 12.

Also, in your 2nd loop you are looping again over Trigger.new, even if you did manage to filter out the accounts with 2+ contacts and added them to a new list, you ignored it by running over Trigger.new again and not over the new list.

0
-2
public class Contactlimit {
    public static void checkInsert(list<Contact> ConAllist){
        list<contact> Conlist = New list<contact>();
         for(Contact c: conAllist){
              if(c.AccountId != Null)
              {Conlist.add(c);}
         }
        if(Conlist.size()>0)
        {checkforConLimit(Conlist);}
    }
    public static void checkUpdate(list<Contact> ConAllist, map<id,Contact> Conoldmap){
        list<contact> Conlist = New list<contact>();
         for(Contact c: conAllist){
              if(c.AccountId != Null && c.AccountId != Conoldmap.get(c.Id).AccountId)
              {Conlist.add(c);}
         }
        if(Conlist.size()>0)
        {checkforConLimit(Conlist);}
    }
     public static void checkforConLimit(list<Contact> Conlist){
        //set of ids of all contacts
        set<id> accids = New set<id>(); 
        for(Contact c: conlist) { accids.add(c.AccountId);}
        if(accids.size()>0){
            //map containing acc id from accid
            Map<id,Account> mapofacc = New Map<Id,Account>([SELECT Id,(SELECT ID from Contacts) From Account where Id IN : accids]);
            for(Contact c: conlist){
                //match trigger contact with its acc id and check size
            if(mapofacc.get(c.AccountId).Contacts.size()>=3)
            {c.addError(System.label.Contact_limit_exceed);}
                                  }     
    }   
    }
}



trigger Abc on Contact (before insert, before update) {
    if(Trigger.isbefore && Trigger.isInsert){
        Contactlimit.checkInsert(Trigger.new);
    }
    if(Trigger.isBefore && Trigger.isUpdate){
        Contactlimit.checkUpdate(Trigger.new,Trigger.oldMap);
    }
    
}
2
  • 1
    Welcome to SFSE. Please check out How to Answer. We look for answers to contain specific description of a solution, not just a code dump. You can make an edit to your post to add a solution, but please note that this question has already been answered several times.
    – David Reed
    Commented Jan 3, 2021 at 19:50
  • Code dumps (which is where there is only code provided, with little to no explanation of how it works) are not good answers. Code can be part of a good answer, but one of the most important things to do is explain what the problem is, and how to solve it. Code dumps tend to generate a lot of back-and-forth in the comments like "that didn't work" and "try this instead" which makes it very hard for other people with similar problems to follow. If you can explain what your code does, edit your answer to include that.
    – Derek F
    Commented Jan 3, 2021 at 19:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .