ApexSensei

Drills › Ship it safely

Build Contacts with one query and one insert

Ship it safely · Intermediate · 18 min · Runs in your Salesforce Developer org · Apex Path

Scenario. A service links new Contacts to parent Accounts. It works for 5 rows, but a 200-row batch would hit the 100-query limit.

Refactor a loop that queries and inserts once per record into one query, a Map lookup and one insert.

What you will learn

A query inside a loop costs one SOQL query per record, and a transaction allows only 100. The fix: run one query with IN, put the rows in a Map, then look each one up inside the loop.

Map<String, Id> idByName = new Map<String, Id>();
for (Account row : [SELECT Id, Name FROM Account WHERE Name IN :names]) {
    idByName.put(row.Name, row.Id);
}

Gotcha. DML inside a loop has the same problem. Build the list first, then insert it once.

The task

Rewrite the slow loop so it finds every parent Account with one query and a Map, then inserts all Contacts at once.

Examples:

  • the starter queries and inserts once per Contact → Queries used: 5 and DML used: 5
  • one query into a Map, then one insert after the loop → Queries used: 1 and DML used: 1
  • every new Contact points at one of the new Accounts → Contacts linked: 5

Requirements:

  1. Before the loop, load the parent Accounts with one query that uses IN.
  2. Put them in a Map from Account Name to Id, and look each parent up there inside the loop.
  3. In the loop, only build Contacts. Insert the list once, after the loop.
  4. Keep measuring from queriesBefore and dmlBefore, and print Queries used: and DML used:.
  5. Count the saved Contacts whose AccountId is one of parents, and print Contacts linked:.

Starter code

String tag = 'AS-SHIP-' + UserInfo.getUserId() + '-' +
    String.valueOf(Datetime.now().getTime());
List<Account> parents = new List<Account>{
    new Account(Name = tag + '-North'),
    new Account(Name = tag + '-South')
};
insert parents;
List<String> parentNames = new List<String>{
    tag + '-North', tag + '-South', tag + '-North', tag + '-South', tag + '-North'
};

Integer queriesBefore = Limits.getQueries();
Integer dmlBefore = Limits.getDmlStatements();
for (String parentName : parentNames) {
    Account parent = [SELECT Id FROM Account WHERE Name = :parentName LIMIT 1];
    insert new Contact(LastName = tag + '-Contact', AccountId = parent.Id);
}
System.debug('Queries used: ' + (Limits.getQueries() - queriesBefore));
System.debug('DML used: ' + (Limits.getDmlStatements() - dmlBefore));

When it passes

One query fills a Map of parent Ids, the loop only builds Contacts, one insert saves them, and all 5 Contacts link to the new Accounts. The grader rolls everything back.

Try this drill free

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

‹ Measure query cost · Gate a release with test evidence ›