SOQL query assigned to a single sObject variable
ID |
apex.correctness.soql_single_row_assignment |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Error Handling |
Language |
Apex |
Description
Reports a SOQL query assigned directly to a single sObject variable rather than to a List. Aggregate queries (SELECT COUNT()) are not reported.
Rationale
Assigning a query to one record asks the platform for exactly one row, and it throws QueryException when it does not get it: "List has no rows for assignment to SObject" when the filter matches nothing, and "List has more than 1 row" when the data grows a duplicate. Neither is catchable where it matters - the exception aborts the transaction, and in a trigger it aborts the user’s save with a message that names Apex rather than anything they did. The query is usually right the day it is written and wrong the day a record is deleted or a second one is created. LIMIT 1 fixes only the second half: zero rows still throws.
The following code illustrates the pattern detected by this rule:
public class AccountLookup {
public Account byId(Id accountId) {
// FLAGGED: SOQL query assigned to a single sObject variable
Account a = [SELECT Id, Name FROM Account WHERE Id = :accountId];
return a;
}
public Contact primaryContact(Id accountId) {
// FLAGGED: SOQL query assigned to a single sObject variable
Contact c = [SELECT Id FROM Contact WHERE AccountId = :accountId LIMIT 1];
return c;
}
public Account byIdSafely(Id accountId) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id = :accountId];
return accounts.isEmpty() ? null : accounts[0];
}
public Integer countAccounts() {
Integer total = [SELECT COUNT() FROM Account];
return total;
}
public Map<Id, Account> byIds(Set<Id> accountIds) {
Map<Id, Account> byId = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);
return byId;
}
}