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
Keerthi GKeerthi G 

Update contact records custom lookup field value

On Contact, based on two custom fields (subgroup id and code), look for Subgroup object record  to get subgroup account field value  then update contact subgroup account look up field value. 
I have the below code working but want to bulkfy as we have millins of contact records.

trigger UpdateSubgroupOnContact on Contact (before insert) {
    if(Trigger.isBefore){
        if(Trigger.isInsert){   
            Set<Decimal> setOfSubgroupIds = new Set<Decimal>();
                for(Contact cnt : trigger.new){
                    if(cnt.SubgroupCode__c != null && cnt.SubgroupId__c != null){
                        setOfSubgroupIds.add(cnt.SubgroupId__c);
                        //system.debug('setof subgroup ids======'+setOfSubgroupIds);
                        //subGroupRecords = [Select Id,Account__c, Subgroup_Id__c, SubGroup_Code__c from SubGroup__c  where Subgroup_Id__c =:cnt.SubgroupId__c];
                        
                   
                    for(List<SubGroup__c> sbg: [Select Id,Account__c,Subgroup_Id__c, SubGroup_Code__c from SubGroup__c  where Subgroup_Id__c IN:setOfSubgroupIds]){
                            for(SubGroup__c s: sbg){
                                cnt.Subgroup__c = s.Account__c;
  
                            }
                        }
                   }     
               }
        }
    }
      
}
Boss CoffeeBoss Coffee
Could you provide clarity as to the relationships between these objects? What is SubGroup's relationship to Contacts?

And could you explain each line of the following for loop?
for(List<SubGroup__c> sbg: [Select Id,Account__c,Subgroup_Id__c, SubGroup_Code__c from SubGroup__c where Subgroup_Id__c IN:setOfSubgroupIds]){
  for(SubGroup__c s: sbg) {
    cnt.Subgroup__c = s.Account__c;
  }
}
Keerthi GKeerthi G
Contact has a lookup to Subgroup object.
Keerthi GKeerthi G
Contact has lookup to Subgroup.
Boss CoffeeBoss Coffee
Try the following. I made some assumptions on types and fields based on the given code. I marked my changes with a comment, so you can edit them as needed.
trigger UpdateSubgroupOnContact on Contact (before insert) {
    if(Trigger.isBefore){
        if(Trigger.isInsert){   
            
            // CHANGE: We will use a map instead, so we can remove this set
            // Set<Decimal> setOfSubgroupIds = new Set<Decimal>();
            
            // CHANGE: Create a Map of SubgroupId -> List of Contacts
            Map<String,List<Contact>> subIdContacts = new Map<String,List<Contact>>();
            // CHANGE: Temporary list for holding values
            List<Contact> tempContacts;
            
            for(Contact cnt : trigger.new){
                if(cnt.SubgroupCode__c != null && cnt.SubgroupId__c != null){
                    
                    // CHANGE: Add to the map by new entry or updating an existing entry
                    if(subIdContacts.contains(cnt.SubgroupId__c)) {
                        tempContacts = subIdContacts.get(cnt.SubgroupId__c);
                        tempContacts.add(cnt);
                        subIdContacts.put(cnt.SubgroupId__c, tempContacts);
                    } else {
                        tempContacts = new List<Contact>();
                        tempContacts.add(cnt);
                        subIdContacts.put(cnt.SubgroupId__c, tempContacts);
                    }
                    
                    // CHANGE: We have a map to track subgroup Ids so we will not use the set
                    // setOfSubgroupIds.add(cnt.SubgroupId__c);
                    
                    //system.debug('setof subgroup ids======'+setOfSubgroupIds);
                    //subGroupRecords = [Select Id,Account__c, Subgroup_Id__c, SubGroup_Code__c from SubGroup__c  where Subgroup_Id__c =:cnt.SubgroupId__c];
                    
                    // CHANGE: The below code needs to be moved outside of the for loop because of the query
                    /*
                    for(List<SubGroup__c> sbg: [Select Id,Account__c,Subgroup_Id__c, SubGroup_Code__c from SubGroup__c  where Subgroup_Id__c IN:setOfSubgroupIds]){
                        for(SubGroup__c s: sbg){
                            cnt.Subgroup__c = s.Account__c;
                            
                        }
                    }
                    */
                }     
            }
            
            // CHANGE: Now that this query is outside of the previous for loop, we will only run it once to grab all subgroups
            for(SubGroup__c sbg: [Select Id,Account__c,Subgroup_Id__c, SubGroup_Code__c from SubGroup__c  where Subgroup_Id__c IN :subIdContacts.keySet()]){
                // Because the subgroup has a SubgroupId field, we can find the contacts via the map we made
                for(Contact con : subIdContacts.get(sbg.SubGroup_Id__c)) {
                    // Then for all the contacts under one subgroup Id, we can add the same Account
                    con.Subgroup__c = sbg.Account__c;
                }
            }
        }
    }
}
Keerthi GKeerthi G
Thank you for the update. I have actually modified it to be as below. Please review.  
trigger UpdateSubgroupOnContact on Contact (before insert) {
    if(Trigger.isBefore){
        if(Trigger.isInsert){   
            Set<Decimal> setOfSubgroupIds = new Set<Decimal>();
                for(Contact cnt : trigger.new){
                    if(cnt.SubgroupCode__c != null && cnt.SubgroupId__c != null){
                        setOfSubgroupIds.add(cnt.SubgroupId__c);
                        
                    }
                }
                    Map<Id,SubGroup__c> mapofSubgroups = new Map<Id,SubGroup__c>([Select Id,Subgroup_Id__c,Account__c from SubGroup__c where Subgroup_Id__c IN:setOfSubgroupIds]);

            for(Contact c : trigger.new){
                for(SubGroup__c sbg :mapofSubgroups.values()){
                    if(c.SubgroupId__c == sbg.Subgroup_Id__c){
                        c.Subgroup__c =sbg.Account__c;
                        
                    }
                }

                }

                   }     
               }
        }
Boss CoffeeBoss Coffee
I believe that should work for your use case. I'm unable to test it because I don't have the model and fields set up in my org. As long as that query has been moved out of the for loop, then it shouldn't hit governor limits.

The only (minor?) concern is your double for loop attempts to check every value until it finds a match, which may be a bit inefficient. The solution would be a map so you know which subgroup belongs to which Contacts without needing a check.
Keerthi GKeerthi G
I thought about the same when relooping on contacts. Thankyou for reviewing :)