home Homebuild Toolsbug_report Errorsmenu_book Guideslightbulb Tipssmart_toy Promptsextension Extensionsfolder_open Resourcesinfo About
search
error

System.LimitException: Too many SOQL queries: 101

What does this error mean?

This error occurs when Apex code attempts to execute more than 100 SOQL queries within a single synchronous transaction. Salesforce enforces this governor limit to prevent any single tenant from monopolizing shared database resources on the multi-tenant platform.

The limit is 100 SOQL queries for synchronous operations and 200 for asynchronous operations (Batch, Queueable, Future, Scheduled).

Common Causes

1. SOQL Queries Inside Loops

The most common cause. If you place a SOQL query inside a for loop, the query executes once per iteration. With 200 records, you hit the limit immediately.

Apex — Bad Example
// ❌ BAD: Query inside a loop
for (Account acc : Trigger.new) {
    // This runs once per record!
    List<Contact> contacts = [
        SELECT Id, Name 
        FROM Contact 
        WHERE AccountId = :acc.Id
    ];
}

2. Recursive Triggers

Triggers that update related records can cause other triggers to fire, each with their own SOQL queries, cascading until the limit is hit.

3. Inefficient Helper Methods

Helper or utility methods that perform queries internally, called repeatedly from trigger handlers or batch classes.

How to Fix It

Solution 1: Move Queries Outside the Loop (Bulkification)

Collect all the IDs first, then make a single SOQL query outside the loop. Use a Map to associate the results back.

Apex — Correct
// ✅ GOOD: Bulkified — single query
Set<Id> accountIds = new Set<Id>();
for (Account acc : Trigger.new) {
    accountIds.add(acc.Id);
}

// One query, all results
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [
    SELECT Id, Name, AccountId 
    FROM Contact 
    WHERE AccountId IN :accountIds
]) {
    if (!contactsByAccount.containsKey(c.AccountId)) {
        contactsByAccount.put(c.AccountId, new List<Contact>());
    }
    contactsByAccount.get(c.AccountId).add(c);
}

// Now loop uses the Map — no queries
for (Account acc : Trigger.new) {
    List<Contact> contacts = contactsByAccount.get(acc.Id);
    // Process contacts...
}

Solution 2: Use Relationship Queries

Combine parent and child queries into a single SOQL statement using relationship (subquery) syntax.

Apex
// ✅ Single query with subquery
List<Account> accounts = [
    SELECT Id, Name, 
        (SELECT Id, Name, Email FROM Contacts)
    FROM Account
    WHERE Id IN :Trigger.newMap.keySet()
];

Solution 3: Prevent Trigger Recursion

Apex
public class TriggerHandler {
    private static Boolean hasRun = false;
    
    public static Boolean hasAlreadyRun() {
        return hasRun;
    }
    
    public static void setHasRun() {
        hasRun = true;
    }
}
lightbulb

Pro Tip: Use Limits.getQueries() and Limits.getLimitQueries() in your debug logs to monitor how many SOQL queries your code uses per transaction.