Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All articles
Development·May 3, 2026·10 min read

SOQL vs SOSL: When to Use Each (with 15 Real-World Query Examples)

Side-by-side syntax, 50K vs 2K row limits, when SOSL is faster, dynamic queries, encrypted-field gotchas, and 15 real queries you can paste into the dev console today.

SOQL vs SOSL — when to use each, with 15 real-world examples

TL;DR

  • SOQL = "give me records from THIS object that match these conditions." One object per query, relationship traversal allowed, structured filtering.
  • SOSL = "search this text across MANY objects at once." Multi-object full-text, fuzzy matching, optimized for the search bar use case.
  • Limits per transaction: SOQL = 50,000 rows · SOSL = 2,000 rows. SOSL queries are also capped at 20 per transaction.
  • Pick SOSL when: the user typed something into a search box. Pick SOQL otherwise.

If you've ever stared at the Developer Console wondering whether to write SELECT Id FROM Account WHERE Name LIKE '%Acme%' or FIND {Acme} IN Name FIELDS RETURNING Account, Contact, Lead, you're hitting the SOQL-vs-SOSL question. They look similar. They're not.

This guide gives you the 30-second decision rule, the syntax cheat sheet, and 15 real working examples for both — plus the limits, gotchas, and when each is genuinely faster.

SOQL vs SOSL — one object with structure vs many objects with text

The 30-second decision

Three tests; first "yes" wins.

  1. Does the user want to find records by typing something into a search box?SOSL.
  2. Are you querying ONE object (with optional relationship traversal)?SOQL.
  3. Are you querying MANY objects at once for the same text?SOSL.

Most production code is SOQL. SOSL is for the search bar — and it's far better at it than SOQL would be.

SOQL — the workhorse

-- Basic
SELECT Id, Name, Industry FROM Account WHERE Industry = 'Software'

-- With relationship (parent → child)
SELECT Id, Name, (SELECT Id, FirstName FROM Contacts) FROM Account WHERE Name = 'Acme'

-- With relationship (child → parent)
SELECT Id, Name, Account.Industry FROM Contact WHERE LastName = 'Smith'

-- Aggregate
SELECT Industry, COUNT(Id) total FROM Account GROUP BY Industry HAVING COUNT(Id) > 5

Strengths:

  • Up to 50,000 rows per transaction (SOSL caps at 2,000).
  • Supports SQL-flavored aggregates: GROUP BY, COUNT, SUM, AVG, MAX, MIN.
  • Relationship queries via dot notation or subquery — fetches related data in one call.
  • Selective filtering on indexed fields uses the database index for sub-second performance.

Weaknesses:

  • One root object per query. Can't say "find 'Acme' in any object."
  • LIKE '%foo%' is not full-text search; it's a slow database wildcard scan that can't use indexes.
  • No fuzzy / phonetic matching. Smith and Smyth are different to SOQL.

SOSL — the search-bar specialist

-- Basic
FIND {Acme} IN Name FIELDS RETURNING Account, Contact, Lead

-- With filter
FIND {acme*} IN ALL FIELDS
RETURNING Account(Id, Name), Contact(Id, Email WHERE CreatedDate > LAST_N_DAYS:30)

-- Phone-style
FIND {(555) 123-4567} IN Phone FIELDS RETURNING Contact, Lead, Account

Strengths:

  • Searches many objects in one query. The RETURNING clause specifies which to bring back.
  • Full-text-style matching with wildcards (*, ?) and phrase search.
  • Uses Salesforce's search index (separate from the database index) — fast on big orgs.
  • Phonetic and stemming for English-language searches (run matches running).

Weaknesses:

  • 2,000-row limit per query (and 20 SOSL queries per transaction).
  • Can't do aggregates.
  • The search index can be ~10–60 seconds behind real-time. Records inserted moments ago may not appear yet.

Side-by-side comparison

AspectSOQLSOSL
ScopeOne object (relationships allowed)Many objects
Row limit50,000 / transaction2,000 / transaction
Query limit100 (sync) / 200 (async)20 / transaction
Best forStructured filters, reports, automationSearch bar, "find anything"
Index usedDatabase (per-field)Search index (separate, async)
AggregatesYes (GROUP BY etc.)No
WildcardLIKE '%text%' (slow, no index)text* (fast, indexed)
PhoneticNoYes (English)
Real-timeYes — query the live DBSlight delay — search index lag

Limits side-by-side: SOQL 50,000 rows vs SOSL 2,000 rows

15 real-world examples

SOQL examples

-- 1. All Accounts in the Software industry
SELECT Id, Name FROM Account WHERE Industry = 'Software'

-- 2. Top-10 largest Opportunities by Amount
SELECT Id, Name, Amount FROM Opportunity ORDER BY Amount DESC LIMIT 10

-- 3. Account with all its Contacts (subquery)
SELECT Id, Name, (SELECT Id, FirstName, LastName FROM Contacts)
FROM Account WHERE Name = 'Acme Corp'

-- 4. Contacts with their Account industry (parent traversal)
SELECT Id, FirstName, LastName, Account.Industry
FROM Contact WHERE Account.AnnualRevenue > 10000000

-- 5. Cases by status with count (aggregate)
SELECT Status, COUNT(Id) total
FROM Case WHERE CreatedDate = THIS_QUARTER
GROUP BY Status

-- 6. Opportunities closing in the next 30 days
SELECT Id, Name, CloseDate, Amount
FROM Opportunity
WHERE StageName != 'Closed Won' AND StageName != 'Closed Lost'
  AND CloseDate <= NEXT_N_DAYS:30

-- 7. Find duplicate emails (HAVING)
SELECT Email, COUNT(Id) c
FROM Contact WHERE Email != null
GROUP BY Email HAVING COUNT(Id) > 1

-- 8. Tasks I own that are overdue
SELECT Id, Subject, ActivityDate
FROM Task
WHERE OwnerId = :UserInfo.getUserId() AND ActivityDate < TODAY AND IsClosed = false

-- 9. Self-relationship (parent Account → child Accounts)
SELECT Id, Name, (SELECT Id, Name FROM ChildAccounts) FROM Account WHERE ParentId = null

-- 10. Dynamic SOQL (Apex)
String soql = 'SELECT Id, Name FROM Account WHERE Industry = :industry';
List<Account> accts = Database.query(soql);

SOSL examples

-- 11. Find "Acme" anywhere in name fields, return Accounts/Contacts/Leads
FIND {Acme} IN Name FIELDS
RETURNING Account(Id, Name), Contact(Id, FirstName, LastName), Lead(Id, Company)

-- 12. Wildcard search starting with "soft"
FIND {soft*} IN ALL FIELDS
RETURNING Account(Id, Name), Opportunity(Id, Name)

-- 13. Phone number search
FIND {(415) 555-1234} IN Phone FIELDS
RETURNING Contact(Id, Name), Account(Id, Name), Lead(Id, Company)

-- 14. Multi-word phrase
FIND {"customer success"}
IN ALL FIELDS
RETURNING Account, Contact, Knowledge__kav

-- 15. Search with filter clause per returned object
FIND {urgent}
IN ALL FIELDS
RETURNING Case(Id, Subject WHERE Status != 'Closed' ORDER BY CreatedDate DESC LIMIT 50),
         Knowledge__kav(Id, Title WHERE PublishStatus = 'Online')

Eight SOQL examples and seven SOSL examples — copy-paste-ready

When SOSL is genuinely faster

In benchmarks on a 50M-row org, SOSL beats SOQL LIKE '%foo%' by 10–100×. The reason: LIKE with a leading wildcard cannot use a database index. The DB scans the whole table. SOSL uses the search index, which is built specifically for this.

Rule: never write WHERE Field LIKE '%text%' against a large object. Either use SOSL or rebuild your query around a leading-edge match (WHERE Field LIKE 'text%', which CAN use the index).

Encrypted field gotcha

If you have Salesforce Shield Platform Encryption on a field, SOQL filtering on that field gets very slow. The platform must decrypt rows in memory before filtering. SOSL on encrypted fields is sometimes blocked entirely depending on the field type.

Workaround: don't filter on encrypted fields at all. Filter on a non-encrypted indexed field first to narrow the result set, then post-process.

Dynamic queries — when and how

Dynamic SOQL/SOSL lets you build the query at runtime in Apex. Use it when the user is choosing fields, objects, or filters at runtime (search builders, report customizers).

// Dynamic SOQL
public List<SObject> runQuery(String objName, String filter) {
  if (!Schema.getGlobalDescribe().containsKey(objName)) {
    throw new IllegalArgumentException('Unknown object');
  }
  String soql = 'SELECT Id FROM ' + String.escapeSingleQuotes(objName);
  if (String.isNotBlank(filter)) {
    soql += ' WHERE ' + filter; // ⚠ validate this!
  }
  return Database.query(soql);
}

Security caution: dynamic SOQL is the #1 source of SOQL injection bugs. Always:

  1. Use bind variables (:value) instead of string concatenation when possible.
  2. String.escapeSingleQuotes() any user-provided text that has to be concatenated.
  3. Validate object/field names against Schema.getGlobalDescribe() before using them.

Common mistakes

  • LIKE '%text%' on a big table. Use SOSL. Always.
  • 20+ OR clauses on one query. Slows the optimizer dramatically. Refactor to multiple queries with collected results, or use IN :collection.
  • Querying inside a loop. This is a governor limits violation waiting to happen — see our cheat sheet.
  • Forgetting the SOSL search lag. Inserted records take 10–60 seconds to be searchable. Don't assert SOSL hits immediately after insert in test classes without Test.setFixedSearchResults.
  • Using SOSL for aggregates. SOSL doesn't support GROUP BY. If you need a count, use SOQL.

Frequently asked questions

Can I combine SOQL and SOSL in one transaction? Yes. Each has its own row + query budget. Mix freely.

Does SOSL work on external objects? Limited. The search index is built per object; external objects need to be specifically configured for global search.

How does this affect Agentforce? Agents call Actions that may issue queries. The same SOQL/SOSL rules apply. Bulk-safe Action design is critical.

Is LIKE ever fast? Yes — when the wildcard is on the right side only (Name LIKE 'Acme%'). The database can use the index for prefix matches. The leading-wildcard form is the slow one.

What about Big Objects and Async SOQL? Big Objects (__b) only support a subset of SOQL — no relationships, no aggregations, no GROUP BY. Async SOQL (deprecated for most cases) is being replaced by Data Cloud federation.

Drop the 15 examples into your dev console tonight. The decision rule sticks faster after you've run them.

Share this article

Sources

Related dictionary terms

Keep reading