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
JimmyMacJimmyMac 

Govonor Limits For Batch Apex?

I think the answer to this is “yes”  but are there any govonor limits to worry about if we are using Batch Apex?

 

If the answer is yes, then in my Staging object I have over 30K rows and I need to loop thru each row and do a SOQL query to find the corresponding object to update.

 

I know we aren’t supposed to do SOQL inside of loops so what is the “best practice” for NOT doing this, how else can I accomplish updating corresponding objects without using SOQL in a loop?

 

Thanks.

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Yes, batch apex is subject to the same limits as regular Apex Code (as you guessed). The difference is in the execute() method; each invocation of execute() gets a new governor limit (all the values are reset back to zero).

Your execute method will be handed 200 records at a time. So instead of having to loop through 30k records, you only have to loop through 200. You'll still need to construct a query to find just the relevant data, instead of trying to query all the rows and performing a query on each one. The usual method for this is through the AQU pattern (aggregate, query, update).

 

At the most basic level, AQU looks like this:

 

Create a Map that contains the query results.

Loop through each source object, and place the key into the Map (aggregate).

Query for each record that matches a value in the map's keyset (query).

Loop through each source object, and perform some sort of update to the record (update).

Commit the changes to the database.

 

Let's walk through this with a generic example:

 

global void execute(Database.batchableContext bc, Contact[] scope) {
Map<String,Account> accounts = new Map<String,Account>();
for(Contact record:scope) {
  accounts.put(record.ExternalAccountName__c,null);
}
for(Account record:[SELECT Id,Name FROM Account WHERE Name IN :accounts.keySet()]) {
  accounts.put(record.name,record);
}
for(Contact record:scope) {
if(accounts.get(record.ExternalAccountName__c)!=null) { record.AccountId = accounts.get(record.ExternalAccountName__c).Id;
} } update scope;
}

This example code reparents all contacts by finding accounts with a name in a custom field with actual accounts in salesforce.com. I'm not handling the scenario of duplicate account names, for example, and in reality, there's other non-code methods of achieving this task (through the Data Loader and External ID values), but this outlines a typical AQU pattern.

All Answers

sfdcfoxsfdcfox

Yes, batch apex is subject to the same limits as regular Apex Code (as you guessed). The difference is in the execute() method; each invocation of execute() gets a new governor limit (all the values are reset back to zero).

Your execute method will be handed 200 records at a time. So instead of having to loop through 30k records, you only have to loop through 200. You'll still need to construct a query to find just the relevant data, instead of trying to query all the rows and performing a query on each one. The usual method for this is through the AQU pattern (aggregate, query, update).

 

At the most basic level, AQU looks like this:

 

Create a Map that contains the query results.

Loop through each source object, and place the key into the Map (aggregate).

Query for each record that matches a value in the map's keyset (query).

Loop through each source object, and perform some sort of update to the record (update).

Commit the changes to the database.

 

Let's walk through this with a generic example:

 

global void execute(Database.batchableContext bc, Contact[] scope) {
Map<String,Account> accounts = new Map<String,Account>();
for(Contact record:scope) {
  accounts.put(record.ExternalAccountName__c,null);
}
for(Account record:[SELECT Id,Name FROM Account WHERE Name IN :accounts.keySet()]) {
  accounts.put(record.name,record);
}
for(Contact record:scope) {
if(accounts.get(record.ExternalAccountName__c)!=null) { record.AccountId = accounts.get(record.ExternalAccountName__c).Id;
} } update scope;
}

This example code reparents all contacts by finding accounts with a name in a custom field with actual accounts in salesforce.com. I'm not handling the scenario of duplicate account names, for example, and in reality, there's other non-code methods of achieving this task (through the Data Loader and External ID values), but this outlines a typical AQU pattern.

This was selected as the best answer
JimmyMacJimmyMac
Thanks so much....Much appreciated.