The SOQL-in-a-Loop Problem
By the end of this lesson, you'll be able to:
- Identify SOQL running inside a loop as an anti-pattern
- Explain exactly why it fails at scale even when it works in testing
- Recognize the pattern across a trigger context specifically
Prerequisites: "The Full List of Limits That Matter"
The anti-pattern, one more time
// DON'T DO THIS
for (Opportunity opp : opportunities) {
Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId]; // a query per iteration!
}
// DON'T DO THIS— this exact example has appeared in Modules 16 and 18 already — this lesson is the dedicated, focused treatment.for (Opportunity opp : opportunities) {— loops over the opportunities.Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId];— a query per iteration.}— closes the loop.
Warning: With 5 opportunities, line 3 runs 5 queries — under the 100-query limit, so it works in a small test. With 150 opportunities (a completely realistic batch size), it throws an uncaught System.LimitException: Too many SOQL queries: 101.
Why it "works" until it suddenly doesn't
This is the specific danger of this anti-pattern: it compiles cleanly, and it passes a quick manual test with a handful of records, giving false confidence. The failure only appears once real data volume crosses the threshold — often not during development, but during a bulk data load or a mass update in production, exactly when it's most disruptive.
Especially dangerous inside a trigger
// Extremely dangerous — Salesforce can invoke a trigger with up to 200 records at once
trigger OpportunityTrigger on Opportunity (before update) {
for (Opportunity opp : Trigger.new) {
Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId]; // 200 queries!
}
}
// Extremely dangerous — Salesforce can invoke a trigger with up to 200 records at once— Module 24's triggers can fire with up to 200 records in a single invocation, from a real bulk data operation.trigger OpportunityTrigger on Opportunity (before update) {— the trigger.for (Opportunity opp : Trigger.new) {— loops over every record in this invocation, which could be up to 200.Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId];— a query inside a trigger's loop is almost guaranteed to exceed the 100-query limit the moment a real bulk update happens — not a hypothetical edge case.}— closes the loop.}— closes the trigger.
Exercise
As a comment, explain why this exact code might pass a quick manual test but fail in production.
Show hint
Think about the difference between 5 records and 200.
The SOQL-in-a-Loop Problem Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
A query inside a loop runs once per iteration instead of once total — harmless with 5 records, a guaranteed failure with 200, and the single most common governor limit mistake in real Apex code.