Why You Should Never Write SOQL Inside a Loop in Apex (With Real-Time Examples)

 

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:

LimitValue
Maximum SOQL Queries100 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 AccountsSOQL Inside LoopBulkified Code
11 Query1 Query
2020 Queries1 Query
5050 Queries1 Query
100100 Queries1 Query
200Governor Limit Risk 1 Query
500Transaction FailsStill 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.

Post a Comment

0 Comments