DML or SOQL executed inside a loop
ID |
apex.correctness.limits_in_loop |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Resource Management |
Language |
Apex |
Description
Reports a DML statement, a query assigned inside a loop body, or a Database.query call written inside a for or while loop. A SOQL for-loop, which queries once and iterates the result in batches, is not reported - it is the recommended shape.
Rationale
Apex allows 100 SOQL queries and 150 DML statements per transaction, and a loop spends one per iteration: the code works on the two records a developer tests with and throws System.LimitException the first time a batch, an import or a bulk update passes 100 rows. The failure arrives in production, in bulk, and rolls back the whole transaction - which is why bulkification is the first thing a Salesforce review looks for.
The following code illustrates the pattern detected by this rule:
public class BulkSafety {
public void unbulkified(List<Account> accts) {
for (Account a : accts) {
// FLAGGED: DML or SOQL executed inside a loop
List<Contact> cs = [SELECT Id FROM Contact WHERE AccountId = :a.Id];
// FLAGGED: DML or SOQL executed inside a loop
update a;
}
}
public void unbulkifiedWhile(List<Account> accts) {
Integer i = 0;
while (i < accts.size()) {
// FLAGGED: DML or SOQL executed inside a loop
insert new Task(WhatId = accts[i].Id);
i++;
}
}
public void soqlForLoop(Set<Id> accountIds) {
List<Contact> toInsert = new List<Contact>();
for (Account a : [SELECT Id, Name FROM Account WHERE Id IN :accountIds]) {
toInsert.add(new Contact(LastName = a.Name));
}
insert toInsert;
}
public void bulkified(List<Account> accts) {
Map<Id, Contact> byAccount = new Map<Id, Contact>();
List<Task> tasks = new List<Task>();
for (Account a : accts) {
tasks.add(new Task(WhatId = a.Id));
}
insert tasks;
System.debug(byAccount);
}
}