ApexSensei

Drills › Debug it

Fix a query inside a loop

Debug it · Intermediate · 15 min · Runs in your Salesforce Developer org · Apex Path

Scenario. Support imports 200 Contacts every Monday. The import has failed with Too many SOQL queries: 101 since a new trigger went live.

A Contact trigger looks up each Account one at a time. It passes every test with 5 records and dies on a 200-record import. Bulkify it.

What you will learn

Too many SOQL queries: 101 is the classic production failure. The code is right for one record: one query per record. An import of 200 needs 200 queries, twice the limit of 100.

The fix is always three steps: collect the Ids, run one query into a Map, look each record up in the loop.

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

Gotcha. Collecting Ids and using them are two loops. The query sits between them.

The task

Fix the handler so it runs at most one query however many Contacts are inserted, and still stamps each Contact with its Account's name.

Examples:

  • Contact on Account Acme → Description Works at Acme
  • Contact with no Account → Description stays as it was
  • 200 Contacts on 200 different Accounts → all stamped, one query

Requirements:

  1. Collect the AccountIds of the Contacts first, skipping blanks.
  2. Run one query for all of them into a Map<Id, Account>.
  3. In a second loop, set Description to Works at followed by the Account's Name.
  4. Leave Contacts with no Account, or an Account the query did not return, unchanged.

Starter code

public class AS_A9DF0520ABDF_AccountStamp {
    public static void beforeInsert(List<Contact> contacts) {
        for (Contact contact : contacts) {
            if (contact.AccountId == null) {
                continue;
            }
            Account account = [
                SELECT Name
                FROM Account
                WHERE Id = :contact.AccountId
            ];
            contact.Description = 'Works at ' + account.Name;
        }
    }
}

When it passes

Every Contact with an Account is stamped with its Account's name. The whole insert costs one query, for 2 Contacts or 200.

Try this drill free

You write the Apex yourself. ApexSensei runs it and tells you what passed and what did not.

‹ Fix a query that finds no rows · Fix DML inside a loop ›