Salesforce interview prep · Part 4 of 7

SOQL and SOSL Interview Questions and Answers

SOQL vs SOSL, relationship and aggregate queries, dynamic SOQL and injection, user-mode security and selective queries - with examples.

By Bhanu Pratap SinghLast reviewed 2026-10-2315 min read7 questions1 Beginner3 Intermediate3 Advanced
Level

Almost every Apex question is partly a query question. These cover how to fetch data, how to fetch it safely, and how to fetch it fast enough to survive an org with millions of records.

Q1BeginnerSOQLSOSL

What is the difference between SOQL and SOSL?

1What it is

SOQL queries one object (and its related records) with exact filters - like SQL's SELECT. SOSL searches text across many objects at once using the search index.

2Why it matters

They answer different questions. When you know where the data is, SOQL is precise. When a user types "acme" and it could be an account, a contact or a lead, SOSL finds all of them in one call.

3Example

SOQL - querySELECT Name FROM Account WHERE …Account (+ related)one object, exact filtersyou know where the data isSOSL - searchFIND 'acme' IN ALL FIELDS …AccountContactLeadmany objects, text searchyou know the word, not the place
One of each · Apex
// SOQL: one object, exact filters
List<Account> big = [SELECT Name FROM Account WHERE AnnualRevenue > 1000000];

// SOSL: a word, across several objects
List<List<SObject>> found = [FIND 'Acme*' IN NAME FIELDS
    RETURNING Account(Id, Name), Contact(Id, Name, Email), Lead(Id, Company)];
List<Account> accounts = (List<Account>) found[0];
SOQLSOSL
SearchesOne object and its relationshipsMany objects at once
MatchingExact filters on fieldsText search, with wildcards
Per transaction100 queries (200 async), 50,000 rows20 searches, 2,000 records each

4In short - say this in the interview

SOQL queries one object with exact filters, like SQL. SOSL searches a word across many objects through the search index. SOQL when you know where the data is, SOSL when you only know the word.

Common mistake

Using SOSL to fetch records by Id or exact field values. That is SOQL's job, and SOSL results depend on the search index, which updates with a short delay.

Likely follow-ups
  • Why might a newly created record not appear in SOSL straight away?
  • What does IN NAME FIELDS change?
Q2IntermediateSOQLRelationships

How do you query related records in SOQL?

1What it is

Two directions. Child to parent uses dot notation (Contact.Account.Name). Parent to child uses a subquery on the child relationship name (SELECT … FROM Contacts).

2Why it matters

One relationship query replaces a query per record - fewer queries, fewer loops, and the related data arrives already attached to each record.

3Example

AccountContact 1Contact 2Contact 3child → parentSELECT Account.NameFROM Contactparent → childSELECT Name,(SELECT … FROM Contacts)Up the tree with a dot; down the tree with a subquery on the plural relationship name.
Both directions · Apex
// Child to parent: walk up with a dot (up to five levels)
List<Contact> contacts = [SELECT Name, Account.Name, Account.Owner.Name FROM Contact];

// Parent to child: a subquery on the relationship name
List<Account> accounts = [SELECT Name, (SELECT LastName, Email FROM Contacts) FROM Account];
for (Account a : accounts) {
    for (Contact c : a.Contacts) { System.debug(a.Name + ' - ' + c.LastName); }
}

// A custom relationship ends in __r: (SELECT Amount__c FROM Invoice_Lines__r)

4In short - say this in the interview

Child to parent with dot notation, like Contact.Account.Name. Parent to child with a subquery on the plural relationship name, like (SELECT LastName FROM Contacts), or the __r name for custom relationships.

Common mistake

Writing the subquery against the object name (FROM Contact) instead of the relationship name (FROM Contacts).

Likely follow-ups
  • Where do you find a relationship's name? (On the lookup field definition, "Child Relationship Name".)
  • Do subquery rows count toward the 50,000 row limit? (Yes.)
Q3IntermediateSOQLAggregates

How do aggregate queries work in SOQL?

1What it is

SOQL can calculate in the database: COUNT, SUM, AVG, MIN, MAX with GROUP BY and HAVING. The results come back as AggregateResult rows that you read by field name or alias.

2Why it matters

Counting or summing in the query is far cheaper than loading every record and adding them up in Apex - less heap, less CPU, one round trip.

3Example

Pipeline by stage, this year · Apex
List<AggregateResult> rows = [
    SELECT StageName, COUNT(Id) deals, SUM(Amount) total
    FROM Opportunity
    WHERE CloseDate = THIS_YEAR
    GROUP BY StageName
    HAVING SUM(Amount) > 100000
];
for (AggregateResult r : rows) {
    System.debug(r.get('StageName') + ': ' + r.get('deals') + ' deals, ' + r.get('total'));
}

4In short - say this in the interview

COUNT, SUM, AVG, MIN and MAX with GROUP BY and HAVING, returned as AggregateResult rows read with get() by alias. It moves the arithmetic into the database instead of loading every record into Apex.

Common mistake

Forgetting that the records being aggregated still count toward the 50,000 query-row limit.

Likely follow-ups
  • What is the difference between WHERE and HAVING?
  • What does COUNT() without a field return?
Q4AdvancedSOQLSecurity

What is SOQL injection, and how do you prevent it?

1What it is

An attack where text a user typed is pasted into a dynamic query string and changes the query itself - reading or filtering records the code never meant to expose.

2Why it matters

Dynamic SOQL (Database.query) is needed for search screens and configurable filters, which is exactly where user input meets the query. One unescaped quote is enough.

3Example

Wrong: user text pasted into the query
String q = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';
List<Account> rows = Database.query(q);   // input like  x' OR Name != '  matches everything
Right: a bind variable
List<Account> rows = Database.queryWithBinds(
    'SELECT Id FROM Account WHERE Name = :name',
    new Map<String, Object>{ 'name' => userInput },
    AccessLevel.USER_MODE);

In static SOQL, a bind like WHERE Name = :userInput is always safe. String.escapeSingleQuotes() is the fallback when a value really must be concatenated.

4In short - say this in the interview

User input concatenated into a dynamic query can change the query. Prevent it with bind variables - static SOQL with :variable, or Database.queryWithBinds - and use String.escapeSingleQuotes only when concatenation cannot be avoided.

Common mistake

Escaping quotes but still building field or object names from user input. Check those against a list of allowed names.

Likely follow-ups
  • How do you safely let a user choose the sort field?
  • What does AccessLevel.USER_MODE add?
Q5AdvancedSOQLSecurity

How do you enforce object and field permissions in SOQL and DML?

1What it is

Apex runs in system mode and ignores the user's object and field permissions unless you ask. WITH USER_MODE on a query (and as user on DML) enforces them; Security.stripInaccessible removes the fields a user cannot see while keeping the rest.

2Why it matters

Without it, an LWC backed by Apex can show a salary field to someone whose profile hides it. Sharing keywords do not help - they cover record access only.

3Example

Three tools · Apex
// 1. Enforce sharing, object and field permissions in the query
List<Contact> rows = [SELECT Name, Email FROM Contact WITH USER_MODE];

// 2. The same for DML
insert as user new Contact(LastName = 'Rao');

// 3. Keep what the user may see, drop the rest instead of failing
SObjectAccessDecision d = Security.stripInaccessible(
    AccessType.READABLE, [SELECT Name, Email, Salary__c FROM Contact]);
List<Contact> safe = (List<Contact>) d.getRecords();

4In short - say this in the interview

WITH USER_MODE on SOQL and "as user" on DML make Apex respect the user's sharing, object and field permissions. Security.stripInaccessible quietly removes fields the user cannot access when you would rather degrade than fail.

Common mistake

Relying on with sharing for field security. with sharing is record access only.

Likely follow-ups
  • How does WITH USER_MODE differ from the older WITH SECURITY_ENFORCED?
  • When is system mode the right choice?
Q6AdvancedSOQLPerformance

What is a selective query, and why does it matter on large objects?

1What it is

A query whose WHERE clause filters on an indexed field and narrows the result to a small share of the records - so the database can use the index instead of scanning the whole table.

2Why it matters

On large objects, a non-selective query is slow, and inside a trigger on an object with more than 200,000 records Salesforce rejects it outright with a "non-selective query" error.

3Example

Selective (uses an index)Not selective
WHERE Id IN :idsWHERE Name LIKE '%acme%' (leading wildcard)
WHERE CreatedDate = LAST_N_DAYS:7WHERE Status__c != 'Closed' (negation)
WHERE External_Id__c = :codeWHERE Region__c = null (on an unindexed field)
WHERE OwnerId = :userId AND IsClosed = falseNo WHERE clause at all

Indexed by default: Id, Name, OwnerId, CreatedDate, SystemModstamp, RecordTypeId, lookup and master-detail fields, and fields marked External ID or Unique. Use the Query Plan tool in the Developer Console to check.

4In short - say this in the interview

A selective query filters on an indexed field - Id, Name, CreatedDate, lookups, external IDs - and returns a small fraction of the records, so it uses the index. Non-selective queries are slow, and in triggers on objects over 200,000 records they fail.

Common mistake

Using leading wildcards or != filters on large objects and assuming LIMIT makes it fast. The scan happens before the LIMIT.

Likely follow-ups
  • How do you request a custom index? (Salesforce Support, or mark a field External ID.)
  • What is the Query Plan tool?
Q7IntermediateSOQLHeap

What is a SOQL for loop, and when do you use FOR UPDATE?

1What it is

A SOQL for loop iterates over query results in chunks of 200 instead of loading them all into one list. FOR UPDATE locks the selected records until the transaction ends.

2Why it matters

The loop keeps heap usage low when a query returns thousands of large records. FOR UPDATE prevents two transactions changing the same record at once - two payments against one balance, for example.

3Example

Both · Apex
// Heap-friendly: 200 records at a time
for (List<Account> chunk : [SELECT Id, Description FROM Account
                            WHERE CreatedDate = LAST_N_DAYS:30]) {
    // work with this chunk; earlier chunks can be freed
}

// Lock the row so no one else changes the balance mid-transaction
Account acc = [SELECT Id, Balance__c FROM Account WHERE Id = :accountId FOR UPDATE];
acc.Balance__c -= payment;
update acc;

4In short - say this in the interview

A SOQL for loop processes results 200 at a time so the heap stays small. FOR UPDATE locks the selected rows until the transaction commits, so two transactions cannot change the same record at once.

Common mistake

Running DML inside the SOQL for loop for each record. Collect changes per chunk and update the chunk.

Likely follow-ups
  • What happens if a locked record is requested by another transaction? (It waits, then fails with UNABLE_TO_LOCK_ROW.)
  • What is the maximum OFFSET in SOQL? (2,000.)

Answers describe documented behaviour, checked against the sources listed. Platforms change between releases - confirm limits and versions before an interview.