4

I have a requirement for a process that would delete qualified records from multiple objects. There are about 5 objects and criteria for deletion is the same for all objects. I want to do all of it in one batch class, possibly by iterating through each object, adding records for deletion to one common list and then perfom a delete. Is this possible? Can someone please help with the syntax?

Thank you!

3
  • Will you already know the ID's of the records you want to delete? Jun 25, 2015 at 15:52
  • I wouldn't know the id of the records i'd need to delete. I have to query for those records first.
    – user9862
    Jun 25, 2015 at 17:46
  • @user9862. If you liked the idea, it would be useful for other developers if you mark it as the solution ;) Jun 26, 2015 at 7:12

4 Answers 4

6

You can combine up to 10 types of SObject in one list for DML as long as they are grouped by type. See this post by Jesse Altman for more color on that.

You can accomplish what you are looking for by using an Iterable. Take a look here for a good example of how to use an Iterable in a batch (at the bottom). You will want to iterate over a List<SObject>. In your constructor, add your various queries one by one to this list.

Rough example:

global class CustomIterable implements Iterator<SObject>
{
    final List<SObject> records;
    global CustomIterable()
    {
        records = new List<SObject>();
        records.addAll((List<SObject>)[SELECT Id FROM Account]); // WHERE...
        records.addAll((List<SObject>)[SELECT Id FROM Contact]); // WHERE...
        records.addAll(Database.query('SELECT Id FROM Opportunity')); // WHERE...
    }

    // rest of implementation
}

This general approach should get you started.

4
  • Adrian, thank you for your response! Is there a limit to the amount of records returned when using the iterator?
    – user9862
    Jun 25, 2015 at 17:47
  • 1
    Not that I know of, at least within a single iteration. Your total record count will be subject to LimitQueryRows, which is 50,000.
    – Adrian Larson
    Jun 25, 2015 at 17:52
  • Adrian, I tried this but getting "Incompatible argument type List<Account> for All method on List<SObject>
    – sf_user
    Dec 14, 2015 at 17:21
  • 1
    @pmvsdt I updated with a couple different ways to get around that. You can cast the list if you want to use standard queries, or use Database.query, which returns a generic List<SObject> already.
    – Adrian Larson
    Dec 14, 2015 at 17:24
5

Here's how I decided to implement my requirement. Still need to test it, but high level seems to be working:

global class DeleteRecords implements Database.Batchable<string>, Database.Stateful  {
global boolean bReRun = false; //will be used to determine if batch has to re-run in case there are more that 10K of records
global Iterable<string> start(Database.BatchableContext ctx) {
    return new list<String> { 'object1', 'object2', 'object3','object4','object5'}; //list of strings with my object names
}
global void execute(Database.BatchableContext ctx, list<string> lstsObjectName) {
    list<sObject> lstDeleteRecords = new list<sObject>();
    for(string strObjectName : lstsObjectName) {
        for(sObject objsObject : database.query('Select Id from ' + strObjectName + ' where for_delete__c = TRUE')) {
            if(lstDeleteRecords.size() < 9998)
                lstDeleteRecords.add(objsObject);
            else {
                bReRun = true;
                break;
            }
        }
    }
    lstDeleteRecords.sort();
    delete lstDeleteRecords;
}
global void finish(Database.BatchableContext ctx) {
    if(bReRun) {
         Database.executebatch(new DeleteRecords());
    }
}

}

1

Another option is to call batches in chain, so you can get advantage of Database.queryLocator that is able to return more records than Iterator.

On each chain you can query the object you want to remove and delete it. You would be able to set different scopeSize for each one as well if this is required. Maybe objectA is more complex than objectB

public with sharing class MyBatch implements Database.Batchable<sObject>, Database.Stateful
{
    private Integer currentChain;

    private Integer scopeSize;

    public MyBatch()
    {
        currentChain = 1;
    }

    public MyBatch(Integer chain)
    {
        currentChain = chain;
    }

    public Database.QueryLocator start(Database.BatchableContext BC)
    {
        String qry;

        if(currentChain == 1)
        {
            qry = 'Select Id From ObjectA__c';
        }
        else(currentChain == 2)
        {
            qry = 'Select Id From ObjectB__c';
        }

        return Database.getQueryLocator(qry);
    }

    public void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        delete scope;       
    }

    public void finish(Database.BatchableContext BC)
    {
        if(currentChain == 1)
        {
            MyBatch mb = new MyBatch(currentChain++);
            Database.execute(mb, scopeSize);
        }
        else(currentChain == 2)
        {
            sendEmail();
        }
    }
}
5
  • You should probably wrap that DML in some sort of try/catch.
    – Adrian Larson
    Jun 25, 2015 at 16:30
  • Agustina, thank you for your response. The concept is simple to understand and i think this is the route I'll be trying to implement for my requirement.
    – user9862
    Jun 25, 2015 at 17:49
  • Also you should really use a list or map to track which chain you're in instead of those if/else chains. With 10 separate objects that would get really ugly.
    – Adrian Larson
    Jun 25, 2015 at 17:54
  • Agree with that, will do!
    – user9862
    Jun 25, 2015 at 18:01
  • Of course!! @user9862 I didn't include any error handler or way to recovery from an issue with rollback, or sendEmails. Just left the code as simpler as possible, so developer can include all their fantastic ideas. Batch is not so simple every ;) Jun 26, 2015 at 7:11
1

Replace your start with the following:

global Iterable<sObject> start(Database.BatchableContext bc) {

        // Database.getQueryLocator([Select Name  from blah LIMIT 10000]);
        List<sObject> lstUPQs = [select Id from Account where ... LIMIT 5000];
        List<sObject> lstJRs = [select Id from Contact where ... LIMIT 5000];
        
        List<sObject> lstAll = new List<sObject>();
        lstAll.addAll(lstUPQs);
        lstAll.addAll(lstJRs);
        
        return lstAll;
    }

 global void execute(Database.BatchableContext bc, List<sObject> scope){
       
        delete scope;
    }  

You must log in to answer this question.

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