SOQL vs SOSL: Which One Your Query Actually Needs
SOQL reads what you know to find. SOSL searches what you don't. Here's how to pick the right Salesforce query language every time, with Apex you can paste and run.
TL;DR
Use SOQL when you know which object holds the data and what fields you need. Use SOSL when you have a search box and need matches across many objects in one round trip. SOQL returns one list of one object type. SOSL returns a list of lists and caps each result at 2,000 rows. If you pick wrong, you either write ten queries or write one query that fails silently.
The One-Line Difference
SOQL is a lot like SQL, but built for one object at a time:
List<Account> hot = [
SELECT Id, Name
FROM Account
WHERE Rating = 'Hot'
LIMIT 10
];SOSL is built for search. You give it a term, a scope, and a list of objects to return. It hits them all in one call:
List<List<SObject>> found = [
FIND 'Acme*'
IN ALL FIELDS
RETURNING Account(Id, Name), Contact(FirstName, LastName)
LIMIT 10
];That is the whole shape of the choice. One of them is a filter. The other is a search. Pick the tool that matches the question.
When SOQL Is the Right Call
Reach for SOQL when you can name the object up front. "Show me every Account in California rated Hot" is a SOQL question. "Find every Contact under Account A" is a SOQL question. "Give me the count of Opportunities by Stage" is a SOQL question.
SOQL is the workhorse for a reason. It supports everything you would expect from a query language:
WHEREwithAND,OR,IN,LIKEORDER BYandLIMIT- aggregate functions like
COUNT(),SUM(),AVG() - parent-to-child and child-to-parent traversal through relationships
- date literals like
THIS_QUARTERandLAST_N_DAYS:30
Here is a parent-to-child query that a SOQL question would naturally produce:
List<Account> withContacts = [
SELECT Id, Name,
(SELECT Id, LastName FROM Contacts)
FROM Account
WHERE Industry = 'Technology'
LIMIT 25
];
System.debug(withContacts.size());A SOSL query cannot do any of that. It has no WHERE Industry = 'Technology'
in the strict sense, no aggregates, no parent-to-child subqueries. If your
question needs those, you are writing SOQL.
When SOSL Is the Right Call
Reach for SOSL when you have a search box and you do not yet know which object the answer lives on. The classic case is a Salesforce global search bar. A user types "Acme" and expects to see Accounts, Contacts, and Opportunities that match. Three SOQL queries could do that. One SOSL query does it in a single round trip and one governor-limit count.
SOSL has its own grammar:
| Part | What it does | Example |
|---|---|---|
FIND | The search term, in single quotes | FIND 'Acme*' |
IN | Which fields to search across | IN ALL FIELDS |
RETURNING | Which objects to return and what fields | RETURNING Account(Name) |
LIMIT | Max rows per object, up to 2,000 | LIMIT 10 |
Search groups are ALL FIELDS, NAME FIELDS, EMAIL FIELDS, PHONE FIELDS,
and SIDEBAR FIELDS. ALL FIELDS is the default. Despite the name, it does
not literally scan every column on a standard object. It scans the searchable
text fields, plus name, email, phone, and sidebar fields. That is enough for
almost every global-search bar.
Here is a real SOSL that searches three objects at once:
List<List<SObject>> results = [
FIND 'Acme*'
IN ALL FIELDS
RETURNING
Account(Id, Name WHERE Industry = 'Technology' LIMIT 5),
Lead(Id, FirstName, LastName, Company LIMIT 5),
Opportunity(Id, Name, StageName LIMIT 5)
];
for (List<SObject> row : results) {
System.debug(row);
}Two important things to notice. First, the result is List<List<SObject>>.
The outer list has one entry per object you named in RETURNING. The inner
list is the records for that object. You almost always loop the outer list
and cast each inner list to the type you expect.
Second, each object can have its own WHERE, ORDER BY, and LIMIT. SOSL
filters per object, not across the whole result. That is how you keep a
search bar from returning 2,000 of one object and zero of the rest.
The Limits That Bite
SOQL and SOSL have different ceilings, and forgetting which is which is the fastest way to hit a governor limit.
| Cap | SOQL | SOSL |
|---|---|---|
| Max rows returned | 50,000 per query | 2,000 per object |
| Aggregate queries | Yes | No |
| Search-term length | n/a | 10,000 chars (logical operators dropped past 4,000) |
| Number of objects in one call | 1 (plus relationships) | Many |
The 50,000 SOQL cap rarely bites. The 2,000 SOSL cap bites all the time. If
you run a SOSL without a LIMIT and one of your objects has thousands of
matches, SOSL silently stops at 2,000 and the user has no idea they are
seeing a slice.
Wildcards also work differently. SOSL supports * (zero or more chars,
middle or end) and ? (one char, middle or end). SOQL LIKE uses % and
_. Mixing those up is a one-character bug that reads like a missing-result
bug.
// SOSL wildcard: zero or more characters at the end
List<List<SObject>> wide = [
FIND 'John*'
IN NAME FIELDS
RETURNING Contact(Id, FirstName, LastName)
];
// SOQL LIKE wildcard: same idea, different syntax
List<Contact> narrow = [
SELECT Id, LastName
FROM Contact
WHERE LastName LIKE 'John%'
LIMIT 100
];Dynamic SOSL When You Need It
Sometimes the search term is not known until runtime. Apex binds SOSL
variables with the : colon, the same as SOQL, but only in static SOSL.
For anything that builds the query from user input, build a string and call
Search.query():
public static List<List<SObject>> findAcrossObjects(String term) {
String safe = String.escapeSingleQuotes(term);
String sosl =
'FIND \'' + safe + '*\' IN ALL FIELDS ' +
'RETURNING Account(Id, Name), Contact(Id, FirstName, LastName)';
return Search.query(sosl);
}Two details that matter here. First, the search term has to escape single
quotes, otherwise user input like O'Brien breaks the SOSL string.
String.escapeSingleQuotes is the right call. Second, the * wildcard is
appended to the term, which is the common pattern for a "starts with"
search bar. If you build SOSL by hand, these two details are the ones that
turn into production incidents.
How To Pick, In Practice
A simple rule holds almost every time:
- I know the object. SOQL.
- I do not know the object. SOSL.
- I need aggregates, joins, or strict filters. SOQL.
- I need one call that spans many objects. SOSL.
If you are still not sure, write the question down in one sentence. If the sentence starts with "show me every ..." you want SOQL. If it starts with "find anything that ..." you want SOSL.
Where To Practice Next
If you want to write real SOQL against a live org, the
SOQL challenges walk you through the
queries that come up most in PD1 prep and on the job. If you want a quick
tour of how Salesforce hands you back List<List<SObject>> from SOSL,
the Apex Collections lesson covers lists,
sets, and maps with runnable snippets.
About Warren Walters
Salesforce MVP and transformative mentor with 8+ years in the Salesforce realm. Founder of Lightning Challenge, dedicated to nurturing the next generation of Salesforce talent through hands-on practice and real-world coding challenges.
Visit Profile →