Why You Should Never Write SOQL Inside a Loop in Apex (With Real-Time Examples)
If you're new to Salesforce Apex, you've probably written code that works perfectly during testing—but suddenly fails when real users or integrations start processing hundreds of records.
One of the biggest reasons this happens is writing SOQL queries inside a loop.
At first, this may seem harmless. After all, the code compiles, runs, and returns the expected results. But in a production environment, this small mistake can bring an entire transaction to a halt.
In this blog, we'll understand why SOQL inside a loop is dangerous, how it affects performance, and the correct way to write scalable Apex code.
What is SOQL?
SOQL (Salesforce Object Query Language) is used to retrieve records from Salesforce objects.
Example:
List<Account> accounts = [ SELECT Id, Name FROM Account ];
This query retrieves all Account records.
Simple enough.
The problem starts when we execute this query repeatedly inside a loop.
The Beginner Mistake
Suppose you want to retrieve Contacts for every Account.
Many beginners write code like this:
for(Account acc : Trigger.new){ List<Contact> contacts = [ SELECT Id, LastName FROM Contact WHERE AccountId = :acc.Id ]; }
At first glance, nothing seems wrong.
The query works.
The loop works.
Everything looks fine.
But let's see what actually happens behind the scenes.
What Happens Internally?
Imagine the trigger receives:
- 1 Account
The query runs 1 time.
Everything is fine.
Now imagine Data Loader inserts:
- 50 Accounts
The query executes:
50 times.
Still okay.
Now imagine an integration inserts:
- 150 Accounts
The query executes:
150 times.
Salesforce immediately throws an exception.
The Error You'll See
System.LimitException: Too many SOQL queries: 101
Once this happens:
- Entire transaction fails
- No records are saved
- All DML operations rollback
- Users receive an error
Even if 100 records were processed successfully before the error, Salesforce rolls back everything.
Why Salesforce Has This Limit
Salesforce is a multi-tenant platform.
Thousands of companies share the same servers.
Imagine if one organization executed millions of database queries in a single transaction.
Other organizations would experience slow performance.
To prevent this, Salesforce introduces Governor Limits.
One important limit is:
| Limit | Value |
|---|---|
| Maximum SOQL Queries | 100 per synchronous transaction |
That means your Apex code cannot execute more than 100 SOQL queries during one transaction.
Real-Time Scenario
Let's say your company has a nightly integration.
Every night:
- ERP sends 500 Account records
- Salesforce Trigger runs
- Trigger queries Contacts inside the loop
What happens?
500 Accounts
↓
500 SOQL Queries
↓
Governor Limit exceeded
↓
Entire integration fails
↓
Business users don't receive updated data
All because of one query inside a loop.
Why It Works During Testing
Many developers wonder:
"It worked in Developer Console. Why did Production fail?"
Because during testing, they inserted:
- 1 record
- 2 records
- maybe 5 records
The governor limit was never reached.
Production processes hundreds of records simultaneously.
That's when the issue appears.
The Correct Approach
Instead of querying inside the loop,
collect all IDs first.
Example:
Set<Id> accountIds = new Set<Id>(); for(Account acc : Trigger.new){ accountIds.add(acc.Id); }
Now execute one query.
List<Contact> contacts = [ SELECT Id, LastName, AccountId FROM Contact WHERE AccountId IN :accountIds ];
Now Salesforce performs only:
One SOQL Query
instead of hundreds.
Even Better — Use a Map
Maps make record lookup extremely fast.
Map<Id,List<Contact>> contactMap = new Map<Id,List<Contact>>(); for(Contact con : contacts){ if(!contactMap.containsKey(con.AccountId)){ contactMap.put(con.AccountId,new List<Contact>()); } contactMap.get(con.AccountId).add(con); }
Now you can easily retrieve Contacts for any Account.
for(Account acc : Trigger.new){ List<Contact> accountContacts = contactMap.get(acc.Id); }
Fast.
Clean.
Bulkified.
Performance Comparison
| Number of Accounts | SOQL Inside Loop | Bulkified Code |
|---|---|---|
| 1 | 1 Query | 1 Query |
| 20 | 20 Queries | 1 Query |
| 50 | 50 Queries | 1 Query |
| 100 | 100 Queries | 1 Query |
| 200 | Governor Limit Risk | 1 Query |
| 500 | Transaction Fails | Still 1 Query |
This is why experienced Salesforce developers always bulkify their code.
What is Bulkification?
Bulkification means writing Apex code that works efficiently whether Salesforce processes:
- 1 record
- 10 records
- 50 records
- 200 records
without changing the logic or hitting governor limits.
Every Trigger should assume multiple records may arrive together.
Common Mistakes
Avoid these patterns:
SOQL inside a loop
for(Account acc : accounts){ Contact c = [ SELECT Id FROM Contact WHERE AccountId=:acc.Id LIMIT 1 ]; }
DML inside a loop
for(Account acc : accounts){ update acc; }
Instead:
update accounts;
One DML statement.
Much better.
Best Practices
Always remember these rules:
- Never write SOQL inside a loop.
- Never perform DML inside a loop.
-
Use
Set<Id>to collect IDs. -
Use
Map<Id,SObject>for quick lookups. - Query all required records at once.
- Write code assuming Salesforce may process 200 records in one transaction.
- Always think about governor limits before writing Apex.
Interview Question
Question
Why should we avoid SOQL inside a loop?
Answer
Because every iteration executes a separate database query. Salesforce allows only a limited number of SOQL queries in a transaction. If the limit is exceeded, Apex throws System.LimitException: Too many SOQL queries: 101, causing the entire transaction to fail. The recommended approach is to collect record IDs, execute a single SOQL query using the IN operator, and use Maps or Lists to process the results efficiently.
Key Takeaways
- SOQL inside a loop may work during development but often fails in production.
- Governor limits exist to protect Salesforce's shared infrastructure.
- Bulkification is essential for writing scalable Apex code.
-
Using
Set,Map, and a single SOQL query dramatically improves performance and reliability. - Following this pattern helps you build production-ready applications and avoid one of the most common Apex mistakes.
Conclusion
Writing SOQL inside a loop is one of the easiest mistakes to make—and one of the most important habits to break. As your Salesforce org grows and processes larger volumes of data, efficient Apex becomes critical.
By collecting IDs first, querying records in bulk, and using collections like Set and Map, you can write Apex code that is faster, cleaner, and ready for real-world business scenarios.
Whether you're preparing for a Salesforce interview or building enterprise applications, mastering this concept will make you a stronger Apex developer.
0 Comments