I'm writing a batch which needs to complete similar logic on both Task and Event and is completely suitable for batch processing however I understand I'm unable to query for both in the QueryLocator and pass them through to the execution stage.

So with that being said what is the best practice for conducting the second query and not running into query limits?


2 Answers 2


Well, @Adrian's solution is certainly a good option. I would probably go down the inheritance path since it's what I feel more comfortable with, but I'm not sure which would be a better solution. For example:

global ActivityBatch implements Database.Batchable<sObject> {

    public virtual String query; // This one we will inherit and override in child classes.

    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator(query);

    global void execute(Database.BatchableContext BC, List<sObject> scope) {
       // Do your logic dinamically with SObjects, not events nor tasks.

    global void finish(Database.BatchableContext BC) { }

global EventBatch extends ActivityBatch {
    public override query; // Your query for Events.

global TaskBatch extends ActivityBatch {
    public override query; // Your query for Tasks.

On the one hand, I would argue that it keeps the logic centralized in a parent class while the child classes only have to worry about which records they have to work with. Furthermore, it's more scalable because it doesn't matter how many records you work with, one batch will bother about events and the other about tasks, while at the same time you will only have one logic base in the parent class.

On the other hand, it does require a slightly more complex structure than the other one presented. I'd stick with inheritance, but I'm not sure which is a better solution.


If you're iterating fewer than 50k records, you can just return an iterator (in this case List<SObject>).

public List<SObject> start(Database.BatchableContext context)
    List<SObject> records = new List<SObject>();
    records.addAll(Database.query('SELECT ... FROM Event'));
    records.addAll(Database.query('SELECT ... FROM Task'));
    return records;

Or you could support just a single type at a time by specifying the SObjectType in your constructor:

final SObjectType activityType;
public MyBatch(SObjectType activityType)
    this.activityType = activityType;

public Database.QueryLocator start(Database.BatchableContext context)
    return Database.getQueryLocator('SELECT ... FROM ' + activityType);

I'm not sure there is really a "best practice" in this regard.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .