[SalesForce] First error: Too many query rows: 50001

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);
}}

Best Answer

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
}
Related Topic