1

I have a read query in a batch class which in orgs with a lot of data can bring back over 50,000 records and violate the governor limit. I am trying to hard delete records. I am using the class below to do so which is getting this error:

First error: Too many query rows: 50001

My question is it possible to avoid this governor limit 50001?

global with sharing class UtilizationDetail_DeleteBatch implements Database.Batchable<pse__Utilization_Detail__c>, Database.Stateful {
public String query;
private set<ID> hardDeletionList = new set<ID>();
private list<pse__Utilization_Detail__c> utilizations = new list<pse__Utilization_Detail__c>();

global UtilizationDetail_DeleteBatch() {
    //constuctor 
    this.query = query;
    //@future helper method to retrieve all utilization details
    retrieveIDS(this.hardDeletionList);

    for (ID id: hardDeletionList) {
        String objectStr = 'pse__Utilization_Detail__c';

        // Convert to schema.sObjectType
        Schema.SObjectType convertType = Schema.getGlobalDescribe().get(objectStr);

        // Create an instance of that type
        Sobject genericObject = convertType.newSObject();

        // or if I know it is pse__Utilization_Detail__c !! why making Sobject ?
        pse__Utilization_Detail__c util = (pse__Utilization_Detail__c)convertType.newSObject();
        //switch and assign the record id
        util.Id = id;

        utilizations.add(util);
    }
    System.debug('Utilization details converted:'+utilizations.size());

}

global Iterable<pse__Utilization_Detail__c> start(Database.BatchableContext bc)
{
    //query to return all utilization details records   
    return utilizations;
} 

global void execute(Database.BatchableContext info, list<pse__Utilization_Detail__c> ids)
{     
    System.debug('## deleting '+ids.size()+' utilization details records');   

    //delete list of expired Case Share records
    delete ids;   
    Database.emptyRecycleBin(ids);  
}

global void execute(SchedulableContext sc)  
{   
    //execute the batch
    UtilizationDetail_DeleteBatch deleteCS = new UtilizationDetail_DeleteBatch();
    ID batchprocessid = Database.executeBatch(deleteCS);
}

global void finish(Database.BatchableContext BC){

}//END finish

@future(callout=true)
private static void retrieveIDS(set<ID> ids){
    ids = (new Map<Id, pse__Utilization_Detail__c>(
        [SELECT Id FROM pse__Utilization_Detail__c 
        WHERE pse__Utilization_Calculation__r.Name = 'Region Utilization - Scheduled Weekly-DO NOT USE'])).keySet();
    System.debug('Region Utilization Details - COUNTS:'+ids.size());
}}

global class UtilizationDetail_BatchScheduler implements Schedulable {
global void execute(SchedulableContext info) {
    UtilizationDetail_DeleteBatch b = new UtilizationDetail_DeleteBatch(); 
    database.executeBatch(b, 10);
}}
8

While it won't fix your issue, never use the global access modifier unless you know you need it.


Take advantage of the QueryLocator pattern and iterate over your query itself:

Instead of:

global with sharing class MyBatch implements Database.Batchable<MyObject__c>
{
    global Iterable<MyObject__c> start(...)
    {
        // query ids
    }
    // remainder of implementation
}

Use:

public with sharing class MyBatch implements Database.Batchable<MyObject>
{
    public Database.QueryLocator start(...)
    {
        return Database.getQueryLocator([
            SELECT ... FROM MyObject__c WHERE ...
        ]);
    }
    // remainder of implementation
}
  • Thanks @Adrian Larson. The upgrade worked. Now, how iterate over 50K records without hitting the governor limit? Could you help me please? Or might don't need because depend on executeBatch.size()? – kitsunebrave Mar 6 '18 at 16:28
  • That's what the batch machinery does for you. It loops over 200 at a time unless you specify a different batch size (max 2000). – Adrian Larson Mar 6 '18 at 16:33
  • Am I able to apply Dan Appleman solution to chain my batch job from finish method of another batch job? I saw this discussion salesforce.stackexchange.com/questions/88890/… – kitsunebrave Mar 6 '18 at 20:01
  • @dave Please do not ask follow up questions in the comments. If you want to ask more questions, post new ones. – Adrian Larson Mar 6 '18 at 20:06
2

The answer that suggests using QueryLocator is a good and scalable answer. If you're in a hurry, you could also do this by using the LIMIT clause.

SELECT Name, ID
From Contact
Where Mailing_State = 'TX'
LIMIT 50000

If you're using the code on a one time basis for cleanup, this approach is probably fine. If it's going to run regularly in production, go with the QueryLocator approach.

  • Nothing wrong with using 50000 instead of 49999, though. – sfdcfox Mar 6 '18 at 16:13
  • Thx @sfdcfox. Maybe I asked this twice but am I able to apply chain batch operations in my batch class above? – kitsunebrave Mar 6 '18 at 20:04

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.