Salesforce developers often encounter difficulties when working with large datasets in Apex. Retrieving too many records simultaneously produces governor limit errors, while inefficient processing methods result in degraded system performance. Apex Cursors serve as a solution to managing large datasets by enabling efficient record retrieval and processing within Salesforce resource boundaries.
The Salesforce Summer ’24 release introduced Apex cursors (Beta) as a tool for developers to handle extensive SOQL query results inside a single transaction. Apex cursors provide a method to retrieve data incrementally, which helps decrease memory usage and stops governor limit errors.
In this article, we will focus on practical use cases where Apex cursors significantly improve query performance, data processing efficiency, and system scalability.
Use Case #1: Pagination for Large Data Retrieval in UI
Scenario:
A Salesforce Lightning Web Component (LWC) needs to display thousands of Contact records in a paginated list.
Solution:
Use Apex cursors with fetch() to implement server-side pagination.
public class ContactPagination {
private Database.Cursor cursor;
private Integer pageSize = 10;
private Integer currentPage = 0;
public ContactPagination() {
cursor = Database.getCursor('SELECT Id, Name FROM Contact ORDER BY Name');
}
public List<Contact> getNextPage() {
List<Contact> contacts = cursor.fetch(currentPage * pageSize, pageSize);
currentPage++;
return contacts;
}
public Boolean hasMore() {
return (currentPage * pageSize) < cursor.getNumRecords();
}
}
Why Use Apex Cursors?
- Improves UI performance by fetching only a limited number of records per page instead of loading everything at once.
- Ensures smooth user experience in LWC without hitting SOQL row limits.
Use Case #2: Optimized Data Export for External Systems
Scenario:
A third-party system needs millions of Opportunity records for reporting, but retrieving them in a single query would hit heap size limits.
Solution:
Use Apex cursors with fetch() to process and export records in chunks.
public class DataExport {
public static void exportOpportunities() {
Database.Cursor cursor = Database.getCursor('SELECT Id, Name, Amount FROM Opportunity WHERE StageName = \'Closed Won\'');
Integer batchSize = 500;
Integer totalRecords = cursor.getNumRecords();
for (Integer i = 0; i < totalRecords; i += batchSize) {
List<Opportunity> opportunities = cursor.fetch(i, batchSize);
// Send opportunities to an external system (e.g., API call)
}
}
}
Why Use Apex Cursors?
- Prevents heap size errors when exporting large datasets.
- Efficiently fetches records in batches, ensuring seamless data export.
Use Case #3: Processing Large Data Sets in Asynchronous Jobs
Scenario:
A developer needs to update all high-value accounts asynchronously without blocking user actions or hitting CPU limits.
Solution:
Use Queueable Apex with Apex Cursors to break large updates into smaller, asynchronous jobs.
public class ProcessHighValueAccountsQueueable implements Queueable {
public void execute(QueueableContext context) {
Database.Cursor cursor = Database.getCursor('SELECT Id, Name FROM Account WHERE AnnualRevenue > 1000000');
Integer batchSize = 200;
Integer totalRecords = cursor.getNumRecords();
for (Integer i = 0; i < totalRecords; i += batchSize) {
List<Account> batchRecords = cursor.fetch(i, batchSize);
for (Account acc : batchRecords) {
acc.Name = acc.Name + ' - Priority';
}
update batchRecords;
}
}
}
Why Use Apex Cursors?
- Asynchronous execution prevents CPU timeout errors.
- Efficiently processes high-value records in controlled batches.
Use Case #4: Precomputing Reports & Dashboards Data
Scenario:
A Salesforce org with large amounts of sales data needs precomputed insights for dashboards instead of calculating metrics in real-time.
Solution:
Use Apex cursors with scheduled jobs to aggregate data periodically.
global class AggregateSalesDataScheduler implements Schedulable {
global void execute(SchedulableContext sc) {
Database.Cursor cursor = Database.getCursor('SELECT SUM(Amount) totalSales FROM Opportunity WHERE CloseDate = THIS_YEAR');
Integer totalSales = 0;
while (cursor.hasNext()) {
List<AggregateResult> results = cursor.fetch(0, 10);
for (AggregateResult ar : results) {
totalSales += (Decimal) ar.get('totalSales');
}
}
System.debug('Total Sales for this year: ' + totalSales);
}
}
Why Use Apex Cursors?
- Precomputes sales metrics, reducing real-time dashboard query loads.
- Ensures fast and responsive reporting for business users.
Apex cursors revolutionize large-scale data processing in Salesforce, making it easier to handle high-volume record sets efficiently. By using fetch(), getCursor(), and batch processing techniques, developers can prevent heap size issues, improve UI performance, and optimize reporting systems.
Which use case resonates with your work? Share your experiences and thoughts in the comments!
#Salesforce #Apex #DataProcessing #ApexCursors