For a simplified example, assume that all our accounts have a ranking based on revenue. Every month I want to update these rankings based on new accounts that were added. Assume we have 50,000 accounts.
The logic is pretty simple, create a list of all the accounts, order by revenue DESC, loop through the list and use the index as the ranking.
However, I'm hung up on doing this at a large scale. It can't be done through a trigger since it gives me a 10,001 DML error. I've tried Apex Batch Update, but that seems to only load 200 records at a time before applying the above logic. However, I need to run the logic on all the records at once. I'm not very familiar with Batch Apex so maybe there is a different way to load the data.
How can this be done?
Thanks
Below is my existing code.
global class BatchTierUpdate implements Database.Batchable<sObject> {
global final String Query;
global BatchTierUpdate()
{
Query = 'SELECT Id FROM Account Order by Revenue__c';
}
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<Account> scope) {
Integer i = 1;
For (Account ac : scope) {
ac.Rank__c = i;
i = i + 1;
}
update scope;
}
global void finish(Database.BatchableContext BC) {
}
}
Best Answer
What you need here is to implement Database.Stateful on your class to retain the index between calls to the execute method, without this the index will reset each time Batch Apex passes the job the next chunk of 200 records (which is the default chunk/scope size via Database.executeBatch).
If you move your Integer out as a class member this will retain its state between calls, like so.
Note: That i have also removed the global and used public, the documentation still shows this, as in the past it was a requirement, these days it is not, unless you want to expose this class to callers outside a managed package.