With Apex cursors, you can break up the processing of a SOQL query result into pieces that can be processed within the bounds of a single transaction. Cursors provide you with the ability to work with large query result sets, while not actually returning the entire result set. You can traverse a query result in parts, with the flexibility to navigate forward and back in the result set. Package developers and advanced developers can use cursors effectively to work with high-volume and high-resource processing jobs. Cursors are an alternative to batch Apex and address some of batch Apex’s limitations. Cursors are also more powerful because they can be used in a chain of queueable Apex jobs.
How: Apex cursors are stateless and generate results from the offset that is specified in the Cursor.fetch(integer position, integer count)
method. You must track the offsets or positions of the results within your particular processing scenario.
A cursor is created when a SOQL query is executed on a Database.getCursor()
or Database.getCursorWithBinds()
call. When a Cursor.fetch(integer position, integer count)
method is invoked with an offset position and the count of records to fetch, the corresponding rows are returned from the cursor. The maximum number of rows per cursor is 50 million, regardless of the operation being synchronous or asynchronous. To get the number of cursor rows returned from the SOQL query, use Cursor.getNumRecords()
.
Apex cursors throw these new System exceptions: System.FatalCursorException
and System.TransientCursorException
. Transactions that fail with System.TransientCursorException
can be retried.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | public class QueryChunkingQueuable implements Queueable { private Database.Cursor locator; private integer position; public QueryChunkingQueuable() { locator = Database.getCursor ( 'SELECT Id FROM Contact WHERE LastActivityDate = LAST_N_DAYS:400' ); position = 0 ; } public void execute(QueueableContext ctx) { List<Contact> scope = locator.fetch(position, 200 ); position += scope.size(); // do something, like archive or delete the scope list records if (position < locator.getNumRecords() ) { // process the next chunk System.enqueueJob( this ); } } } |
Apex cursors have the same expiration limits as API Query cursors.
To get Apex cursor limits, use these new methods in the Limits
class.
-
Limits.getApexCursorRows()
and its upper boundLimits.getLimitApexCursorRows()
method -
Limits.getFetchCallsOnApexCursor()
and its upper boundLimits.getLimitFetchCallsOnApexCursor()
method
These Apex governor limits have been updated with this feature.
-
Maximum number of rows per cursor: 50 million (both synchronous and asynchronous)
-
Maximum number of fetch calls per transaction: 10 (both synchronous and asynchronous)
-
Maximum number of cursors per day: 10,000 (both synchronous and asynchronous)
-
Maximum number of rows per day (aggregate limit): 100 million