Too Many SOQL Queries: Multiple triggers are firing due to a DML operation

Too Many SOQL Queries: Multiple triggers are firing due to a DML operation

On February 14, 2024, Posted by , In Salesforce, With Comments Off on Too Many SOQL Queries: Multiple triggers are firing due to a DML operation

The “Too Many SOQL Queries: 101” error in Salesforce can also occur when multiple triggers are firing on the same object or related objects, each executing SOQL queries. This can quickly accumulate and exceed the governor limit of 100 SOQL queries per transaction.

Example: Account and Contact Triggers

Suppose you have two triggers: one on the Account object and another on the Contact object. Both triggers execute SOQL queries.

Account Trigger:

trigger AccountTrigger on Account (before update) {
    for (Account acc : Trigger.new) {
        List<Contact> contacts = [SELECT Id, Name FROM Contact WHERE AccountId = :acc.Id];
        // Process contacts
    }
}

Contact Trigger:

trigger ContactTrigger on Contact (before update) {
    for (Contact con : Trigger.new) {
        Account acc = [SELECT Id, Name FROM Account WHERE Id = :con.AccountId];
        // Process account
    }
}

If an update operation on Account causes updates on related Contact records (which is common in Salesforce), both triggers will fire, and each will execute SOQL queries within a loop. This can quickly lead to the “Too Many SOQL Queries: 101” error if the number of affected records is large.

Solution: Bulkify the Code and Use Trigger Framework

To prevent this error, you should bulkify the code in both triggers and consider using a trigger framework to control the order and execution of the triggers.

Revised Account Trigger:

trigger AccountTrigger on Account (before update) {
    Set<Id> accountIds = new Set<Id>();
    for (Account acc : Trigger.new) {
        accountIds.add(acc.Id);
    }

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

    // Process contacts by account
}

Revised Contact Trigger:

trigger ContactTrigger on Contact (before update) {
    Set<Id> accountIds = new Set<Id>();
    for (Contact con : Trigger.new) {
        accountIds.add(con.AccountId);
    }

    Map<Id, Account> accountsMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);

    // Process accounts for contacts
}

In the revised triggers, SOQL queries are moved outside the loops, and maps are used to store the queried data. This reduces the number of SOQL queries and helps avoid hitting the governor limit. Additionally, implementing a trigger framework can help manage the execution order and prevent redundant operations.

Comments are closed.