Loading
ただいま大変多くのお問い合わせをいただいており、ご連絡までにお時間を頂戴しております続きを読む

Batch Apex Error: 'Aggregate Query Does Not Support queryMore()' — Causes and Solutions

公開日: May 22, 2026
説明

In Salesforce Batch Apex, the start() method uses a Database.QueryLocator to retrieve records for processing. Internally, Salesforce uses the queryMore() method to paginate through large result sets returned by the QueryLocator.


However, aggregate SOQL queries — those that use clauses such as GROUP BY, MIN(), MAX(), COUNT(), or SUM() — do not support queryMore(). Attempting to use an aggregate query directly in a Batch Apex start() method causes the following error:

"Aggregate query does not support queryMore(), use LIMIT to restrict the results to a single batch."

This article describes two supported patterns for working around this limitation.

解決策

Option 1: Run the Aggregate Query in a Schedulable Class Using @ReadOnly

This approach separates the aggregate query from the Batch class. A Schedulable class runs the aggregate SOQL query using the @ReadOnly annotation, which bypasses standard governor limits on query rows and allows large result sets to be queried.

The aggregate result is passed as a parameter to the Batch class constructor. The Batch class then uses a standard non-aggregate SOQL query in its start() method, filtered by the value obtained from the aggregate query.


For example, a Schedulable class annotated with @ReadOnly runs a query such as SELECT MIN(CreatedDate) CD FROM Account using aggregate syntax. It extracts the result value from the AggregateResult list and passes it to the Batch class constructor. The Batch class stores this value as an instance variable and uses it in a standard SOQL query in start(), such as SELECT Id FROM Account WHERE CreatedDate = :dt.

Note: The code patterns below are illustrative examples. Modify and test in a sandbox before deploying to production.

Schedulable Class: Implement the Schedulable interface and annotate the execute() method with @ReadOnly. Run the aggregate query, extract the result value, and call Database.executeBatch() passing the result to the Batch class constructor.


Batch Class: Implement Database.Batchable. Accept the aggregate result value in the constructor. In start(), use Database.getQueryLocator() with a standard SOQL query filtered by the value received from the Schedulable class.

Option 2: Implement the Iterable Interface Instead of QueryLocator

This approach replaces the Database.QueryLocator return type in start() with a custom Iterable implementation. The Batch class implements Database.Batchable and returns a custom Iterable from start(). The custom Iterable class implements Iterable and returns a custom Iterator. The Iterator class runs the aggregate SOQL query directly, stores the results in a list, and implements hasNext() and next() methods to iterate over the AggregateResult records.


Iterable Class: Implements Iterable. The Iterator() method returns an instance of the custom Iterator class.
Iterator Class: Implements Iterator. The constructor runs the aggregate SOQL query (e.g., SELECT Id, MIN(CreatedDate) FROM Account GROUP BY Id LIMIT 1) and stores the results. The hasNext() method checks whether more results remain, and next() returns the next AggregateResult.


Batch Class: Implements Database.Batchable. The start() method returns a new instance of the Iterable class. The execute() method processes each AggregateResult in the batch scope.

ナレッジ記事番号

000386516

 
読み込み中
Salesforce Help | Article