Process Automation and Logic · Topic 3 of 7
SOQL, SOSL and DML in Apex
SOQL and SOSL in Apex with bind variables, relationship and aggregate queries, date literals, SOSL search, DML statements, Database methods and upsert.
- 4 min
- SOQL reads one object and its relationships; SOSL searches text across objects.
- Bind variables (
:var) prevent SOQL injection. - A query assigned to a single sObject must return exactly one row.
What it is
Apex reads data with two query languages and writes it with DML.
- SOQL (Salesforce Object Query Language) reads records from one object and its related objects, like SQL
SELECT. - SOSL (Salesforce Object Search Language) runs a text search across several objects at once.
- DML (Data Manipulation Language) inserts, updates, upserts, deletes, undeletes and merges records.
How it works
SOQL in Apex
String stage = 'Closed Won';
List<Opportunity> opps = [
SELECT Id, Name, Amount, Account.Name
FROM Opportunity
WHERE StageName = :stage AND CloseDate = LAST_N_DAYS:30
ORDER BY Amount DESC
LIMIT 50
];
:stageis a bind variable; it is safe from SOQL injection.- Assigning a query to a single sObject (
Account a = [SELECT ...];) throws aQueryExceptionif zero or more than one row returns. - Date literals include
TODAY,YESTERDAY,THIS_MONTH,LAST_N_DAYS:n(includes today) andNEXT_N_DAYS:n. IN :idSetfilters by a collection.
Aggregate queries
List<AggregateResult> rows = [
SELECT AccountId, COUNT(Id) total, SUM(Amount) amt
FROM Opportunity
GROUP BY AccountId
HAVING COUNT(Id) > 5
];
for (AggregateResult r : rows) {
Id accId = (Id) r.get('AccountId');
Integer total = (Integer) r.get('total');
}
WHERE filters rows before grouping; HAVING filters groups by an aggregate. OFFSET can skip at most 2,000 rows.
SOSL
List<List<SObject>> found = [
FIND 'Acme*' IN NAME FIELDS
RETURNING Account(Id, Name), Contact(Id, LastName)
];
List<Account> accs = (List<Account>) found[0];
| Use SOQL when | Use SOSL when |
|---|---|
| You know the object and the fields to filter | You search text across several objects |
| You need relationship or aggregate queries | The user types a search term, like a global search |
| Exact matching on fields | Matching words in many fields at once |
A transaction can run 20 SOSL searches, and each search returns at most 2,000 records.
Dynamic queries
Database.query(queryString) runs a SOQL string built at run time. Bind variables still work inside the string (WHERE Name = :term), and are the safe way to include user input. Database.queryWithBinds() takes a map of bind values and an access level.
DML statements and Database methods
| Statement | Database method | On error |
|---|---|---|
insert recs; | Database.insert(recs, false) | Statement: whole list fails and a DmlException is thrown. Method with allOrNone=false: valid records save, errors are returned |
update recs; | Database.update(recs, false) | Same |
upsert recs Field; | Database.upsert(recs, Field, false) | Same |
delete recs; | Database.delete(recs, false) | Same |
undelete recs; | Database.undelete(recs, false) | Same |
Database.SaveResult[] results = Database.insert(contacts, false);
for (Integer i = 0; i < results.size(); i++) {
if (!results[i].isSuccess()) {
System.debug(contacts[i].LastName + ': ' + results[i].getErrors()[0].getMessage());
}
}
merge combines up to three records of the same type (Account, Contact, Lead and some others). Inserting a setup object such as User and a non-setup object such as Account in the same transaction causes a mixed DML error; move one to @future or use System.runAs in tests.
A worked example
A nightly import inserts 200 Contacts, and three fail a validation rule. With insert contacts; all 200 fail and a DmlException is thrown. With Database.insert(contacts, false), 197 save and the three failures appear in the SaveResult array, where the code can log them. If the business rule says "save what is valid and report the rest", the Database method with allOrNone = false is the answer.
A SOQL query inside a for loop is the most common wrong answer in this exam. Query once, before the loop, using IN :ids.
How the exam asks about this
- "Search for a term across Accounts, Contacts and Leads." SOSL.
- "Save valid records and report failures."
Database.insert(records, false). - "Filter by a value the user typed." A bind variable, never string concatenation.
What to remember for the exam
- SOQL reads one object and its relationships; SOSL searches text across objects.
- Bind variables (
:var) prevent SOQL injection. - A query assigned to a single sObject must return exactly one row.
- DML statements are all or none; Database methods with
falseallow partial success. - 20 SOSL searches per transaction, 2,000 records per search; OFFSET up to 2,000.
Account a = [SELECT Id FROM Account WHERE Name = 'X']; throws "List has no rows for assignment to SObject" when nothing matches. Query into a List and check its size.
Common mistakes
Using SOSL to filter one object by exact field values. That is SOQL.
Catching a DmlException to save the valid records. The statement already rolled them back; use allOrNone = false.
Concatenating user input into a dynamic query. Use a bind variable.
Check yourself
Which query language searches text across several objects at once?
SOSL, with
FIND ... RETURNING.What happens when
insert recs;hits one failing record in 200?None of the 200 are saved and a
DmlExceptionis thrown.What is the difference between WHERE and HAVING?
WHERE filters rows before grouping; HAVING filters the groups by an aggregate value.
Topic 3 of 7 in Process Automation and Logic
Test yourself on Process Automation and Logic
The full Salesforce Platform Developer question bank has 645 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by Salesforce. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.