Understanding the SOQL IN Operator and Query Limits
The SOQL IN operator allows you to filter records against multiple values in a single query, making it essential for bulkified Apex code. When used incorrectly, it can trigger the “System.LimitException: Too many SOQL queries: 101” error that occurs when your code exceeds Salesforce’s governor limit of 100 SOQL queries per transaction context.
Error: System.LimitException: Too many SOQL queries : 101
“System.LimitException: Too many SOQL queries: 101” errors occur when you exceed SOQL queries governor limit. The actual limit is 100 SOQL queries in a single synchronous transaction context (200 for asynchronous contexts like Batch Apex).
How SOQL IN Operator Prevents Query Limit Errors
The SOQL IN operator is your primary tool for bulkification. Instead of running multiple queries inside loops, use IN to query multiple records at once:
// BAD: Multiple queries in loop (hits 101 limit quickly)
for(Account acc : accounts) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
// Process contacts
}
// GOOD: Single query with IN operator
Set<Id> accountIds = new Set<Id>();
for(Account acc : accounts) {
accountIds.add(acc.Id);
}
List<Contact> allContacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds];
// Governor Limits Check:
// - 1 SOQL query used (limit: 100 synchronous)
// - Up to 50,000 records returned (query row limit)
SOQL IN Operator Syntax and Examples
The IN operator accepts collections (List, Set) or subqueries. Here are production-ready patterns:
Basic SOQL IN Syntax
// Using Set of Ids (most common pattern)
Set<Id> accountIds = new Set<Id>{'001xx000003DHPh', '001xx000003DHPi'};
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
// Using List of strings
List<String> industries = new List<String>{'Technology', 'Healthcare', 'Finance'};
List<Account> techAccounts = [SELECT Id, Name FROM Account WHERE Industry IN :industries];
SOQL IN with Subqueries
// Find accounts with contacts created in last 30 days
List<Account> accounts = [
SELECT Id, Name, Industry
FROM Account
WHERE Id IN (
SELECT AccountId
FROM Contact
WHERE CreatedDate = LAST_N_DAYS:30
AND AccountId != null
)
AND IsDeleted = false
];
SOQL OR vs IN Operator: When to Use Each
While both SOQL OR and IN operators filter records, they serve different purposes:
| SOQL IN Operator | SOQL OR Operator |
|---|---|
| Single field, multiple values | Multiple fields or complex conditions |
| Better performance for large value sets | More flexible for different field comparisons |
| Bulkification-friendly | Can lead to query complexity |
// Use IN for single field, multiple values
SELECT Id FROM Account WHERE Industry IN ('Tech', 'Finance', 'Healthcare')
// Use OR for different fields or complex logic
SELECT Id FROM Account WHERE (Industry = 'Tech' AND AnnualRevenue > 1000000)
OR (Type = 'Customer' AND Rating = 'Hot')
Resolving Too Many SOQL Queries Error
Follow these patterns to stay within the 100-query limit:
1. Bulkify with SOQL IN Collections
// Collect all IDs first, then query once
public static void processAccountContacts(List<Account> accounts) {
Set<Id> accountIds = new Set<Id>();
for(Account acc : accounts) {
accountIds.add(acc.Id);
}
// Single query instead of loop queries
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for(Contact con : [SELECT Id, AccountId, Email FROM Contact WHERE AccountId IN :accountIds]) {
if(!contactsByAccount.containsKey(con.AccountId)) {
contactsByAccount.put(con.AccountId, new List<Contact>());
}
contactsByAccount.get(con.AccountId).add(con);
}
// Process using the map
for(Account acc : accounts) {
List<Contact> relatedContacts = contactsByAccount.get(acc.Id);
if(relatedContacts != null) {
// Process contacts for this account
}
}
}
2. Use Asynchronous Processing
For operations that might exceed limits, use @future or Queueable Apex (200 query limit):
@future
public static void processLargeDataSet(Set<Id> recordIds) {
// 200 SOQL queries available in async context
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
// Process accounts
}
SOQL Includes vs IN Operator
The SOQL includes operator works with multi-select picklists, while IN works with any field type:
// INCLUDES for multi-select picklists
SELECT Id FROM Account WHERE Products__c INCLUDES ('Product A', 'Product B')
// IN for regular fields
SELECT Id FROM Account WHERE Industry IN ('Technology', 'Finance')
// IN with multi-select picklists checks exact matches
SELECT Id FROM Account WHERE Products__c IN ('Product A;Product B', 'Product C')
Governor Limits and Best Practices
Apex runs on a multi-tenant platform where the runtime engine enforces limits to prevent code from monopolizing shared resources. Key limits for SOQL queries:
- Synchronous context: 100 SOQL queries per transaction
- Asynchronous context: 200 SOQL queries per transaction
- Query rows: 50,000 records per SOQL query
- Total rows: 50,000 records retrieved per transaction
Production Best Practices
- Always bulkify: collect IDs, then use SOQL IN operator for single queries
- Avoid SOQL queries inside for loops
- Use selective queries with indexed fields (Id, Name, Email, external IDs)
- Consider query plan and performance with large datasets
- Implement proper null checks and exception handling
- Use Database.query() for dynamic SOQL when needed
Common SOQL IN Operator Patterns
Trigger Bulkification Pattern
trigger AccountTrigger on Account (after insert, after update) {
// Collect Account IDs
Set<Id> accountIds = new Set<Id>();
for(Account acc : Trigger.new) {
if(acc.Industry == 'Technology') {
accountIds.add(acc.Id);
}
}
if(!accountIds.isEmpty()) {
// Single query with IN operator
List<Contact> contacts = [
SELECT Id, AccountId, Email
FROM Contact
WHERE AccountId IN :accountIds
AND Email != null
];
// Process contacts
for(Contact con : contacts) {
con.Description = 'Updated via trigger';
}
update contacts;
}
}
Batch Apex with SOQL IN
global class ProcessAccountsBatch implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([
SELECT Id, Name, Industry
FROM Account
WHERE Industry IN ('Technology', 'Finance', 'Healthcare')
AND IsDeleted = false
]);
}
global void execute(Database.BatchableContext bc, List<Account> accounts) {
// Process up to 200 accounts per batch
// 200 SOQL queries available in batch context
}
}
Debugging SOQL Query Limits
Use these techniques to monitor query usage:
// Check current query count
System.debug('SOQL queries used: ' + Limits.getQueries() + '/' + Limits.getLimitQueries());
// Check query rows
System.debug('Query rows: ' + Limits.getQueryRows() + '/' + Limits.getLimitQueryRows());
// In test classes, use Test.startTest() and Test.stopTest() to reset limits
@isTest
static void testBulkProcessing() {
// Setup data
List<Account> accounts = TestDataFactory.createAccounts(200);
Test.startTest(); // Resets governor limits
// Your code here - fresh 100 query limit
AccountProcessor.processAccounts(accounts);
Test.stopTest();
// Assertions
}
What causes the “Too many SOQL queries: 101” error?
This error occurs when your Apex code executes more than 100 SOQL queries in a single synchronous transaction. The most common cause is placing SOQL queries inside for loops instead of using bulkified patterns with the SOQL IN operator.
How does the SOQL IN operator help with bulkification?
The SOQL IN operator allows you to query multiple records in a single SOQL statement by passing a collection of values. Instead of running separate queries for each record in a loop, you collect all IDs first, then use one query with IN to retrieve all related records at once.
What’s the difference between SOQL IN and INCLUDES operators?
SOQL IN works with any field type and checks for exact matches against a list of values. SOQL INCLUDES is specifically for multi-select picklist fields and checks if the field contains any of the specified values, regardless of other selected values.
When should I use SOQL OR instead of the IN operator?
Use SOQL OR when you need to check different fields or create complex conditional logic. Use SOQL IN when filtering a single field against multiple values. IN operator generally performs better for large value sets on the same field.
How many SOQL queries can I run in asynchronous contexts?
Asynchronous contexts (Batch Apex, Queueable, @future methods, Schedulable) allow 200 SOQL queries per transaction, double the synchronous limit of 100. This makes them ideal for processing large datasets that might exceed synchronous limits.
Can I use dynamic SOQL with the IN operator?
Yes, you can use Database.query() for dynamic SOQL with IN operators. Build your query string dynamically and include the IN clause with bind variables. Ensure proper input validation to prevent SOQL injection attacks.