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
IC-TannerIC-Tanner 

Bulk Trigger issue due to range lookup

 

 I am trying to bulk a trigger but have run into issues with the normal set/map method and therefore cannot figure out how to bulk the trigger. 
Quick background:
-before 'lead' object insert, query 'AFS_Zip__c' object to determine what the 'area team' is for the lead based on the lead's postal code referenced against a postal code range on the 'AFS_Zip__c'' object. 
-Normally I would just add all the 'trigger.new' postal codes to a set, then run one SOQL query to obtain all the 'area team' field values from the 'AFS_Zip__c' object based on 'in' set.  Because the 'AFS_Zip__c'' object provides a range of postal codes per 'area team', 'in' doesn't work. I therefore tried to add all the 'AFS_Zip__c' object'object records to a map but ran into  the '1000 record limit on SOQL queries per trigger'. because the 'AFS_Zip__c' object contains 4000+ records. 
Working code but not bulkified:
trigger TrgIns_SetDefaultValues on Lead (before insert,before update) 
{
    integer IsATFound;
    
    IsAtFound = 0;  
    
    for(Lead lead : trigger.new)    
    {       
        for (AFS_Zip__c az : [Select Name from AFS_Zip__c az where az.FromZip__c <= :lead.PostalCode and 
             az.ZipTo__c >= :lead.PostalCode]) 
        { 
            lead.Area_Team__c = az.name; 
            IsATFound = 1;  
        }//close for AFS_zip 
          
        if (IsATFound == 0) 
        { 
            lead.Area_Team__c = 'Un-Repped'; 
        }            
    }    
}

updated code that is running into '1000 record limit on SOQL queries per trigger' limit:
trigger TrgIns_SetDefaultValues_beta on Lead (before insert,before update) 
{
    map<string, string> zipMap = new map<string,string>();
     
    //creates lists of 'AFS_Zip__c' records. Issue is that the total query records size returned is 4000+
    
    for(list<AFS_Zip__c> zipList : [select id, name, FromZip__c, ZipTo__c from AFS_Zip__c])
    {    
        //for each 'AFS_Zip__c' record, determine upper and lower postal code values and 
        //map each value in the range to the 'name' field
        for (AFS_Zip__c zip : zipList)
        {         
            for(integer zipValue = integer.valueOf(zip.FromZip__c); zipValue <= integer.valueOf(zip.ZipTo__c); zipValue++)
            {
                zipMap.put(string.valueOf(zipValue), zip.name);      
            }       
        }
    }      
    
    for(Lead lead : trigger.new)    
    { 
        //if lead zip code is found in the map (~'AFS_Zip__c' postal code range'), then update the lead 'Area_Team__c 
        //field. If not, update it as 'Un-Repped'
        if(zipMap.containsKey(lead.PostalCode))
        {
            lead.Area_Team__c = zipMap.get(lead.PostalCode);
        }
        else
        {
            lead.Area_Team__c = 'Un-Repped'; 
        }  
    }   
}
------------------------------------------
Logically, I need to determine a means of only querying/mapping for the 'postal code' values from the 'trigger.new' but can't determine a method to do this because of the range. I thought about using custom settings to get around the SOQL record limits but the range check doesn't seem possible via the custom settings methods (getAll(), etc.) without using SOQL. 
Any suggestions would be greatly appreciated. Thanks!!

 

Best Answer chosen by Admin (Salesforce Developers) 
MickleMickle

I'm not sure if this will fully solve your problem, but there are different Governor limits depending on how the Trigger is invoked.

 

Example 1: Apex Trigger invoked by the user interface

An Apex trigger has been deployed on the Account object and a user creates a new Account record through the user interface. When the user hits the "Save" button, that record is inserted into the Account table and the trigger is invoked. At this point, Apex calculates the governor limits to apply to the Apex code. All normal trigger limits are applied. Here is a sample (and not a comprehensive list) of those limits:

  • Maximum of 20 SOQL queries
  • Maximum of 1,000 records retrieved by SOQL queries
  • Maximum of 10,000 script statements can be executed.

Example 2: Apex Trigger invoked by a batch of records through the Force.com Web Services API

Let’s extend the previous example where we have an Apex Trigger on the Account object. But instead of a single record being created through the user interface, a batch of 200 records is inserted into the Account table through the Web Services API. That single API call invokes the Apex trigger, but now the trigger is invoked with 200 records. That is important since the 200 records will increase several of the governor limit calculations. For example:

  • Maximum of 20,000 records retrieved by SOQL queries.
  • Maximum of 50,000 script statements can be executed.

Notice how those two sample governor limits are substantially higher since the Apex trigger was invoked due to a batch of records instead of a single record. The number of records that can be retrieved in a SOQL query has scaled 20 times as compared to when the trigger was invoked by a single record through the user interface. This helps illustrate the point that the number of records invoking the Apex impacts the governor limit calculation.

 

-http://wiki.developerforce.com/index.php/Governors_in_Apex_Code

 

Perhaps breaking your code into two sections will alleviate these issues.

 

 

for (Lead l : Trigger.new){
    IF(Trigger.new.size() == 1 ){    
    //DO THE FIRST PART OF CODE THAT WORKS    
    }
    ELSE {    
    //DO THE BULK TRIGGER CODE  
    }
}

 

 

 

All Answers

MickleMickle

I'm not sure if this will fully solve your problem, but there are different Governor limits depending on how the Trigger is invoked.

 

Example 1: Apex Trigger invoked by the user interface

An Apex trigger has been deployed on the Account object and a user creates a new Account record through the user interface. When the user hits the "Save" button, that record is inserted into the Account table and the trigger is invoked. At this point, Apex calculates the governor limits to apply to the Apex code. All normal trigger limits are applied. Here is a sample (and not a comprehensive list) of those limits:

  • Maximum of 20 SOQL queries
  • Maximum of 1,000 records retrieved by SOQL queries
  • Maximum of 10,000 script statements can be executed.

Example 2: Apex Trigger invoked by a batch of records through the Force.com Web Services API

Let’s extend the previous example where we have an Apex Trigger on the Account object. But instead of a single record being created through the user interface, a batch of 200 records is inserted into the Account table through the Web Services API. That single API call invokes the Apex trigger, but now the trigger is invoked with 200 records. That is important since the 200 records will increase several of the governor limit calculations. For example:

  • Maximum of 20,000 records retrieved by SOQL queries.
  • Maximum of 50,000 script statements can be executed.

Notice how those two sample governor limits are substantially higher since the Apex trigger was invoked due to a batch of records instead of a single record. The number of records that can be retrieved in a SOQL query has scaled 20 times as compared to when the trigger was invoked by a single record through the user interface. This helps illustrate the point that the number of records invoking the Apex impacts the governor limit calculation.

 

-http://wiki.developerforce.com/index.php/Governors_in_Apex_Code

 

Perhaps breaking your code into two sections will alleviate these issues.

 

 

for (Lead l : Trigger.new){
    IF(Trigger.new.size() == 1 ){    
    //DO THE FIRST PART OF CODE THAT WORKS    
    }
    ELSE {    
    //DO THE BULK TRIGGER CODE  
    }
}

 

 

 

This was selected as the best answer
IC-TannerIC-Tanner

Good call Mickle! That did the trick!

Pradeep_NavatarPradeep_Navatar

Create a custom auto number type field (suppose should be integer and stating from 1) so that you can query the record on the basis of the auto number.

 

Below is the sample code and I guess it will help you :

 

                Integer First =1;

                Integer Last = 10000;

                Boolean Status = false;

                do

                {

                                ListAcc = [Select id, CustomField__c , AutoNum__c From Account where AutoNum__c >=:First AND AutoNum__c<=:Last];

                                ListListAcc.add(ListAcc);

                                First = First + 10000;

                                Last = Last + 10000;

 

                                If(ListAcc.size()>0)

                                Status = true;

                                else

                                Status = false;

                }

                While(Status == true);

                update ListListAcc;