3

I am under the suspicion that the execute method in a batch apex class does not get called when the scope is null. Is this true? When the scope returns null I still want the execute method to be called.

3 Answers 3

5

The execute() method will not be called if your start method returns either null or an empty iterable. So for example if you implement Database.Batchable<sObject> and your Database.getQueryLocator(query) returns an empty result set, the execute() method will not run.

7
  • 2
    +1, though, in simpler terms, "execute will not run if there's nothing to do."
    – sfdcfox
    Oct 24, 2019 at 15:49
  • Thank you @sfdcfox for a much more eloquent explanation :) Oct 24, 2019 at 15:50
  • @sfdcfox Can I force it to run? Oct 24, 2019 at 15:53
  • No, but you can move the logic currently into your execute() method to another Apex Class, and then in your batch start() method, if you can determine that your query or your iterable collection will be empty, then you can call that other Apex class Oct 24, 2019 at 17:58
  • 1
    @Bahman.A you would not return null. Just an empty iterable object. Dec 31, 2020 at 15:39
2

The system will never run an execute method if there's nothing to run. Also be aware that null values are stripped from the list of elements to process. As a workaround, you could consider using a Wrapper class and returning at least one value. For example:

public Wrapper[] start(Database.BatchableContext context) {
    Wrapper[] results = new Wrapper[0];
    Wrapper temp = new Wrapper();
    for(sObject record: ...) {
        temp.records.add(record);
        if(temp.records.size() == 100) {
            results.add(temp);
            temp = new Wrapper();
        }
    }
    // add the last (partial) batch
    results.add(temp);
    return results;
}

At this point, you've constructed the code in a way that will force at least one iteration of execute, even if there's nothing to do.

However, this solution won't work if you expect you may get more than 50,000 records. You might also be able to use a hybrid solution by returning Iterable<Object> and use a Wrapper if there's less than X records, or a Database.QueryLocator otherwise.

1

If you are using a query locator implementation, you could force execute to be run once by ensuring start:

  1. Runs the exact same query WHERE against the same object but as a COUNT.
  2. If the count is 0 then explicitly call execute with an empty scope
  3. Return the required query locator

This only consumes one additional query per batch execution, and one additional row in start (which is not relevant to the limits applied when execute is called anyway since start/execute/finish are called in different transactions).

2
  • You mean call execute with empty scope?
    – javanoob
    Oct 24, 2019 at 18:18
  • Yes, accidental mention of "start". Corrected. Thanks.
    – Phil W
    Oct 25, 2019 at 6:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.