Finding All SOQL Queries Inside Loops: A Practical Guide to Avoiding Governor Limits
By Kodelens Team
•October 11, 2025

You've just deployed your new feature. It worked perfectly in your sandbox with a handful of test records. The first time a user tries to process a list of 200 records in production, the system grinds to a halt and crashes.
A moment later, an email arrives in your inbox with a subject line you know all too well: System.LimitException: Too many SOQL queries: 101.
You've just committed one of the cardinal sins of Apex development: you've placed a SOQL query inside a loop. This is, without a doubt, the most common cause of governor limit failures in Salesforce. Finding and fixing this performance anti-pattern is a critical skill for every Apex developer.
This guide will show you how to hunt down these performance killers and refactor them for a bulkified, scalable future.
Why is a SOQL Query in a Loop So Bad?
To understand the problem, you have to understand the platform. Salesforce is a multi-tenant environment, meaning your code runs on the same shared servers as thousands of other companies' code. To ensure one tenant doesn't hog all the resources, "governor limits" place strict caps on your code's execution.
- The Governor's Rule: The limit for SOQL queries in a single transaction is 100.
- The Math of Failure: If you have a
forloop that iterates over a list of 200 records from a trigger, and you place a SOQL query inside that loop, your code will try to execute 200 separate queries. It will run successfully for the first 100 records, but on the 101st iteration, it will hit the governor limit and your entire transaction will fail and roll back.
The core principle of scalable Apex development is bulkification: writing your code to handle up to 200 records at a time without ever coming close to limits. For SOQL queries, this means getting all the data you need before you start looping.
The Hunt: How to Find the Hidden Queries
Fixing the problem is usually straightforward. The hard part is finding it, especially in a large, complex codebase.
Method 1: The Manual Code Review (The Slow Way)
You know the trigger or class that failed, so you open the file and start reading, line by line. You look for a for loop, and then you scan inside that loop for any line containing [SELECT ... FROM ...].
But it's rarely that simple. The query might not be directly in the loop. The loop might call a helper method, and that helper method contains the query. This forces you to become a human code analyzer, manually tracing every method call and holding the entire call stack in your head. It's slow, tedious, and easy to miss something.
Method 2: The Kodelens Search (The Fast Way)
Instead of manually tracing code, you can use an intelligent search tool that understands your code's structure. With Kodelens, you can ask a question that understands the context of your code.
In the Kodelens search panel, you can simply ask:
"Show me all SOQL queries inside a loop in my project"
Kodelens analyzes your code's structure and immediately returns a list of every single place where a SOQL query is nested inside a for, while, or do-while loop. It even finds the queries hidden one or two levels deep inside helper methods. It turns a multi-hour investigation into a single, 10-second search.
The Fix: A Common Refactoring Pattern
Once you've found the offending query, fixing it involves a standard, three-step bulkification pattern.
Here is a classic example of the anti-pattern:
// ANTI-PATTERN: DO NOT DO THIS
// This code will fail if Trigger.new contains more than 100 records.
for (Account acc : Trigger.new) {
// SOQL query is INSIDE the loop!
List<Contact> contacts = [SELECT Id, Name FROM Contact WHERE AccountId = :acc.Id];
for (Contact c : contacts) {
// ... do something with the contacts ...
}
}
Here is how you refactor it into a scalable, bulk-safe pattern:
Step 1: Collect IDs Before the Loop
First, iterate through your trigger records once to collect all the relevant IDs you need for your query into a Set.
Step 2: Perform One Query Outside the Loop
Now, perform a single SOQL query outside the loop, using the Set of IDs to get all the related records at once. Store these records in a Map for easy lookup.
Step 3: Process the Data Inside the Loop
Finally, loop through your trigger records again. This time, instead of querying the database, you efficiently access the data you need from the Map you prepared.
Here is the final, bulkified code:
// CORRECT PATTERN: BULKIFIED
// This code will handle up to 200 records with only one SOQL query.
// 1. Collect IDs into a Set
Set<Id> accountIds = Trigger.newMap.keySet();
Map<Id, List<Contact>> contactsByAccountId = new Map<Id, List<Contact>>();
// 2. Perform ONE query outside the loop
for (Contact c : [SELECT Id, Name, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
if (!contactsByAccountId.containsKey(c.AccountId)) {
contactsByAccountId.put(c.AccountId, new List<Contact>());
}
contactsByAccountId.get(c.AccountId).add(c);
}
// 3. Process the data using the Map
for (Account acc : Trigger.new) {
List<Contact> relatedContacts = contactsByAccountId.get(acc.Id);
if (relatedContacts != null) {
for (Contact c : relatedContacts) {
// ... do something with the contacts ...
}
}
}
Conclusion: Think in Bulk
The Too many SOQL queries: 101 error is a rite of passage for Salesforce developers. It's a sign that you're thinking about records one at a time instead of in bulk.
The pattern to fix it is almost always the same: Collect IDs, Query Once into a Map, then Process.
Auditing your entire codebase for this anti-pattern can seem like a daunting task. Using a tool like Kodelens to instantly identify every SOQL query inside a loop can be the first step in refactoring your way to a more scalable, robust, and professional Salesforce organization.

