Boost Your Salesforce Query Performance with @ReadOnly for Large Data

Learn how the Apex @ReadOnly annotation lets you query up to 1,000,000 rows in read-only contexts — perfect for reporting and data exports. This post explains behavior, restrictions, and best practices for Salesforce developers.

The @ReadOnly annotation in Apex relaxes the standard SOQL row limit (50,000) and allows queries that return up to 1,000,000 rows in supported read-only contexts. It’s a powerful tool when you need to process or export very large datasets without modifying records.

What @ReadOnly Does

When you annotate a method with @ReadOnly, Salesforce increases the allowed number of queried rows for that request to 1,000,000. This is ideal for heavy read operations such as reporting, analytics, or bulk exports.

Key points

  • Queries can return up to 1,000,000 rows (instead of the normal 50,000 SOQL limit).
  • It only applies to supported contexts (REST/SOAP web services and Schedulable).
  • The method is strictly read-only — DML and certain system operations are blocked.

Restrictions and Limitations

  • No DML operations: insert, update, delete, undelete are not allowed inside a @ReadOnly method.
  • No asynchronous job enqueueing: calls to System.schedule or queueable/future/Batch submission are blocked.
  • Context limited: Only available for REST and SOAP web services and classes that implement Schedulable.
  • Shared behavior: Governor limits still apply for CPU time and heap size — design queries and processing accordingly.

When to Use @ReadOnly

Common use cases where @ReadOnly helps:

  • Reporting and analytics that require scanning large tables.
  • Data export jobs that read records for backup or external processing.
  • Read-only dashboards or scheduled reads where no record changes are needed.

Example

Simple Apex example that uses @ReadOnly in an AuraEnabled method to return Accounts created in the last year:

public with sharing class LargeDataQueryController {
    @AuraEnabled
    @ReadOnly
    public static List getAccounts() {
        // This query will be read-only and optimized for performance
        return [SELECT Id, Name, Industry FROM Account WHERE CreatedDate >= LAST_YEAR];
    }
}

Best Practices

  • Use selective WHERE clauses and indexed fields to avoid full table scans.
  • Process records in streaming or batched server-side patterns to minimize heap usage.
  • Keep the method strictly read-only — if you need to modify data, use a separate non-@ReadOnly transaction.
  • Test with realistic data volumes in a full or sandbox-like org to validate CPU and heap usage.

References

Salesforce Apex documentation: ReadOnly

Why this matters: For Salesforce admins, developers, and architects, @ReadOnly unlocks scalable read operations so you can build high-volume reports and exports without hitting SOQL row limits — but you must design carefully to respect other governor limits and restrictions.