When it comes to performance in Salesforce, SOQL (Salesforce Object Query Language) is often the most underestimated component. Poorly written SOQL queries can lead to governor limit exceptions, slow page loads, and failed batch jobs.
In this blog post, you’ll learn how to write high-performance, governor-friendly SOQL queries. Whether you’re a developer, architect, or admin working with Apex or automation tools, this guide will help you optimize every line of your SOQL.
Salesforce enforces governor limits to ensure efficient resource usage in its multi-tenant environment. These limits include:
Inefficient SOQL usage is the #1 cause of hitting governor limits. By optimizing your queries, you enhance performance and reduce errors across Apex, Flows, and Integrations.
Before jumping into advanced techniques, master these fundamentals:
SELECT Id, Name FROM Account WHERE CreatedDate = LAST_N_DAYS:30 ORDER BY Name ASC LIMIT 50
Avoid broad queries like SELECT Id FROM Account
. Use filters on indexed fields (e.g., Id, Name, CreatedDate).
Bad:
SELECT Id FROM Contact WHERE LastName != null
Good:
SELECT Id FROM Contact WHERE Email LIKE '%@company.com'
Fetching unnecessary fields increases data load and impacts performance.
Bad:
SELECT * FROM Opportunity
Good:
SELECT Id, StageName, Amount FROM Opportunity
Limit your results when you only need a subset.
SELECT Id FROM Lead WHERE Status = 'Open' LIMIT 10
SOQL supports both parent-to-child and child-to-parent relationships.
SELECT Id, Account.Name FROM Contact WHERE LastName = 'Smith'
SELECT Name, (SELECT LastName FROM Contacts) FROM Account
Avoid overfetching child records, especially in batch operations.
Salesforce query optimizer relies on selective queries. To be selective:
NOT
, !=
, or LIKE '%value%'
The most common anti-pattern in Apex:
for (Account acc : accounts) {List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];}
Set<Id> accountIds = new Set<Id>();for (Account acc : accounts) {accountIds.add(acc.Id);}List<Contact> allContacts = [SELECT Id FROM Contact WHERE AccountId IN :accountIds];
SOQL supports aggregates like COUNT(), SUM(), and GROUP BY for reporting-style queries.
SELECT StageName, COUNT(Id) FROM Opportunity GROUP BY StageName
Use this for dashboards, analytics, or reporting without exporting data.
When working with 1M+ records:
global class LargeAccountProcessor implements Database.Batchable<SObject> {global Database.QueryLocator start(Database.BatchableContext BC) {return Database.getQueryLocator('SELECT Id FROM Account WHERE IsActive__c = true');}global void execute(Database.BatchableContext BC, List<SObject> scope) {// Processing logic}global void finish(Database.BatchableContext BC) {}}
Enable Apex debug logs and search for SOQL_EXECUTE_BEGIN
and SOQL_EXECUTE_END
.
Run it from Setup to identify performance risks in your org.
Mistake | Impact |
---|---|
Using SELECT * | Fetches unused data |
SOQL in loops | Governor limits |
No filters | Full table scan |
Too many fields | Slower performance |
Not testing in sandbox | Unexpected failures |
SOQL is more than just a query language—it’s a performance tool. Writing efficient SOQL helps avoid limits, speeds up transactions, and improves user experience.
Start auditing your existing queries today. Optimize, refactor, and test your SOQL for long-term success.
Have a query you’d like reviewed? Drop it in the comments or check out our SOQL optimization checklist!
Quick Links
Legal Stuff