在Batch Apex中使用Aggregate SOQL统计查询语句及结果
前言
Database.Batchable
Class must implement the global interface method: Iterable
start(Database.BatchableContext) from Database.Batchable
The following sample code will explain this.
/*
* Compile error : Class must implement the global interface method: Iterable<AggregateResult> start(Database.BatchableContext) from Database.Batchable<AggregateResult>
*/
global class SampleAggregateBatch implements Database.Batchable<AggregateResult> {
// The batch job starts
global Database.Querylocator start(Database.BatchableContext bc){
String query = 'SELECT COUNT(Id), AccountId FROM Contact GROUP BY AccountId';
return Database.getQuerylocator(query);
}
// The batch job executes and operates on one batch of records
global void execute(Database.BatchableContext bc, List<sObject> scope){ }
// The batch job finishes
global void finish(Database.BatchableContext bc){ }
}
To fix this, using Apex classes that implement Iterator<AggregateResult>
and Iterable<AggregateResult>
seems the only way for now.
Here is the outline what we should do.
- Create an Apex class implements
Iterator<AggregateResult>
. - Create an Apex class implements
Iterable<AggregateResult>
. - Implementing to
Database.Batchable<AggregateResult>
, and UsingIterable
at start execution in Batch Apex.
Let’s get started.
Iterator Apex class
Create an Apex class named “AggregateResultIterator” with the following source code :
global class AggregateResultIterator implements Iterator<AggregateResult> {
AggregateResult [] results {get;set;}
// tracks which result item is returned
Integer index {get; set;}
global AggregateResultIterator(String query) {
index = 0;
results = Database.query(query);
}
global boolean hasNext(){
return results != null && !results.isEmpty() && index < results.size();
}
global AggregateResult next(){
return results[index++];
}
}
Iterable Apex class
Create an Apex class named “AggregateResultIterable” with the following source code :
global class AggregateResultIterable implements Iterable<AggregateResult> {
private String query;
global AggregateResultIterable(String soql){
query = soql;
}
global Iterator<AggregateResult> Iterator(){
return new AggregateResultIterator(query);
}
}
Batch Apex
Then implement Batch Apex from Database.Batchable<AggregateResult>
, and use Iterable<AggregateResult>
, AggregateResultIterable instead of Database.QueryLocator at start execution, as the following source code :
global class SampleAggregateBatch implements Database.Batchable<AggregateResult> {
// The batch job starts
global Iterable<AggregateResult> start(Database.BatchableContext bc){
String query = 'SELECT COUNT(Id) cnt, AccountId FROM Contact GROUP BY AccountId';
return new AggregateResultIterable(query);
}
// The batch job executes and operates on one batch of records
global void execute(Database.BatchableContext bc, List<sObject> scope){
for(sObject sObj : scope) {
AggregateResult ar = (AggregateResult)sObj;
System.debug('>>>> COUNT : ' + ar.get('cnt'));
}
}
// The batch job finishes
global void finish(Database.BatchableContext bc){ }
}
Operation check
Run SampleAggregateBatch and check debug log.
// Run batch apex
SampleAggregateBatch batch = new SampleAggregateBatch();
Database.executebatch(batch, 200);
// Debug log output
11:36:35.0 (14518157)|USER_DEBUG|[11]|DEBUG|>>>> COUNT : 1
11:36:35.0 (14903974)|USER_DEBUG|[11]|DEBUG|>>>> COUNT : 3
11:36:35.0 (15035196)|USER_DEBUG|[11]|DEBUG|>>>> COUNT : 1
It seems working fine! : )
Enjoy it!
Resource
转载来源:https://www.xgeek.net/zh/salesforce/using-aggregate-soql-queries-results-in-batch-apex/