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;
    }
}

Remediation

Query into a List and decide explicitly what the empty case means - List<Account> a = [SELECT …​]; if (a.isEmpty()) { …​ }. Where the caller can carry on without the record, return null or a default; where it cannot, throw an exception whose message names the record that was missing.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved