Skip to main content

SF-0377 · Scenario · Hard

Can we use FOR UPDATE in SOQL using Database.QueryLocator?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

No. FOR UPDATE is not supported in Database.QueryLocator queries. Try it and Salesforce throws:

“FOR UPDATE clause cannot be used here.”

The reason is structural: FOR UPDATE locks records for the duration of the current transaction. Batch Apex runs each chunk in its own transaction. The lock from start couldn’t realistically span dozens or thousands of separate chunk transactions — so the platform forbids it altogether.

What you tried

public Database.QueryLocator start(Database.BatchableContext ctx) {
    // Compile error: FOR UPDATE not allowed here
    return Database.getQueryLocator(
        'SELECT Id, Status FROM Case WHERE Status = \'New\' FOR UPDATE'
    );
}

How locking actually works in batch

Each execute invocation starts a fresh transaction. Inside that transaction, you can FOR UPDATE if you want:

public void execute(Database.BatchableContext ctx, List<Case> scope) {
    Set<Id> ids = new Map<Id, Case>(scope).keySet();

    // Lock the chunk's records for the duration of *this* transaction
    List<Case> locked = [SELECT Id, Status FROM Case WHERE Id IN :ids FOR UPDATE];

    for (Case c : locked) c.Status = 'Auto-Closed';
    update locked;
}

The lock is held only during the chunk’s transaction. When execute returns, the lock releases. The next chunk acquires its own lock for its own records.

Why per-chunk locking is usually fine

The whole point of FOR UPDATE is to prevent another transaction from modifying a record while you’re working on it. In batch, the records in each chunk are different — so you only need to lock the current chunk’s records, not the entire dataset.

If a user happens to edit a record while its chunk is processing, the lock prevents it. If a user edits a record from chunk 5 while chunk 3 is running, that’s irrelevant — chunk 5 hasn’t started yet.

When you genuinely need cross-chunk locking

If you really do need to lock the entire dataset (rare), batch isn’t the right tool. Options:

GoalTool
Lock & update millions of recordsDon’t. Re-think the design.
Lock & update a few hundred recordsSynchronous code in a single transaction with FOR UPDATE
Process records carefully with optimistic concurrencyAdd a Version__c field, check-and-set in DML
Prevent edits during a long jobUse record-level flag (e.g., Processing__c = true) + validation rule

Common interview follow-ups

  • Does this restriction also apply to Queueable? — Queueable can use FOR UPDATE inside execute (single transaction). It’s fine because Queueable runs one transaction per job — not one per chunk.
  • Can Iterable start use FOR UPDATE? — No. The same restriction applies to the start query regardless of return type.
  • What about SELECT ... WITH SECURITY_ENFORCED? — That’s allowed in start. Only FOR UPDATE is blocked.

Verified against: SOQL and SOSL Reference — FOR UPDATE. Last reviewed 2026-05-17.