質問

I've just recently run into a governor limit using Batch Apex with the AggregateResult object and I was hoping someone could clearly explain this limit. I think batch APEX could support up to 50 million records, but when I use AggregateResult, I hit governor limits even though the total records in the object was about 250,000.

How does the AggregateResult limit apply within batch APEX?

Any explanation on how this limit is applied and how to overcome it would be appreciated. Should I simply avoid using AggregateResult within batch APEX? Or, if I do use it, how do I determine when this limit will be hit?

Here is a simple batch program I wrote using AggregateResult. This runs monthly and I'm aggregating Tasks related to a case by user. How can I determine if this will hit limits?

global class MemberCaseAchievementBatch implements Database.Batchable<sObject> {

public string query =  'select Id, Enhancement_Value__c from Case where RecordType.DeveloperName = \'SF_Enhancement\' AND Stage__c = \'Deployed\' AND ClosedDate = THIS_MONTH';
public Set<Id> caseIds = new Set<Id>();
public Set<Id> allCaseIds = new Set<Id>();
Integer totalCompletedCases = 0;
Integer totalAssignedCases = 0;
Double totalCompletedValue = 0;

global database.querylocator start(Database.BatchableContext BC)
{
    return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, Sobject[] scope)
{
    for(Case c : (List<Case>)scope) {
        caseIds.add(c.Id);
        totalCompletedCases++;
        if(c.Enhancement_Value__c == null) {
            totalCompletedValue = c.Enhancement_Value__c;
        }
        else {
            totalCompletedValue += c.Enhancement_Value__c;
        }
    }

    for(Case c : [select Id from Case where RecordType.DeveloperName = 'SF_Enhancement' AND CreatedDate = THIS_MONTH]) {
        allCaseIds.add(c.Id);
        totalAssignedCases++;
    }

    Map<Id, Member_Goal__c> memberGoalsMap = getMemberGoals();
    List<Member_Case_Achievement__c> achievementsToInsert = new List<Member_Case_Achievement__c>();
    AggregateResult[] results = getMemberAchievementMetrics(caseIds);
    Map<String, AggregateResult> tasksAssigned = getMemberTotalTasksAssigned(allCaseIds);
    Date firstDayOfMonth = System.today().toStartOfMonth();
    Date lastDayOfMonth = firstDayOfMonth.addDays(Date.daysInMonth(firstDayOfMonth.year(), firstDayOfMonth.month()) - 1);

    for(AggregateResult ar : results) {
        Member_Case_Achievement__c mca = new Member_Case_Achievement__c();
        if(memberGoalsMap.containsKey(String.valueOf(ar.get('OwnerId')))) mca.Member_Goal__c = memberGoalsMap.get(String.valueOf(ar.get('OwnerId'))).Id;
        AggregateResult aggResult = tasksAssigned.get(String.valueOf(ar.get('OwnerId')));
        mca.Total_Assigned_Tasks__c = (Integer)aggResult.get('expr0');
        mca.Total_Completed_Tasks__c = (Integer)ar.get('expr1');
        mca.Total_Completed_Task_Value__c = (Double)ar.get('expr0');
        mca.Total_Assigned_Cases__c = totalAssignedCases;
        mca.Total_Completed_Cases__c = totalCompletedCases;
        mca.Total_Completed_Value__c = totalCompletedValue;
        mca.Period_Start_Date__c = firstDayOfMonth;
        mca.Period_End_Date__c = lastDayOfMonth;
        achievementsToInsert.add(mca);      
    }
    if(!achievementsToInsert.isEmpty() && achievementsToInsert.size() > 0) {
        insert achievementsToInsert;
    }
}

global void finish(Database.BatchableContext BC) {

}

private AggregateResult[] getMemberAchievementMetrics(Set<Id> caseids) {
    AggregateResult[] groupedResults = [select OwnerId, SUM(Task_Value__c), Count(Id) from Task where WhatId in: caseids AND Subject in ('Requirements','Design','Develop / Config','Unit Testing') group by OwnerId];
    return groupedResults;
}

private Map<String, AggregateResult> getMemberTotalTasksAssigned(Set<Id> caseids) {
    Map<String, AggregateResult> aggregateResultMap = new  Map<String, AggregateResult>();
    for(AggregateResult ar : [select OwnerId, Count(Id) from Task where WhatId in: caseids AND Subject in ('Requirements','Design','Develop / Config','Unit Testing') group by OwnerId]) {
        aggregateResultMap.put(String.valueOf(ar.get('OwnerId')), ar);
    }
    return aggregateResultMap;
}

private Map<Id, Member_Goal__c> getMemberGoals() {
    Map<Id, Member_Goal__c> memberGoalMap = new Map<Id, Member_Goal__c>();
    for(Member_Goal__c mg : [select Id, Member__c from Member_Goal__c where Goal_Period__r.Period_Start_Date__c = THIS_MONTH]) {
        memberGoalMap.put(mg.Member__c, mg);
    }
    return memberGoalMap;
}

}

Is the limit determined by the scope? Or, is it determined by the AggregateResult query?

役に立ちましたか?

解決

Batch apex allows you to run batches of up to 50 million records. So in your case you could batch over 50M Cases. Normal Apex limits still apply however inside of each batch.

You could try to limit the scope size of each batch via Database.executeBatch(yourBatch, scopeSize); where scopeSize is an Integer between 1 and 2000. Setting this to a lower number (200 is the default) would run fewer Cases per batch, in effect hopefully decreasing the size of your aggregate result too.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top