SOQL IN Operator: Fix Too Many SOQL Queries Error | SalesforceTutorial

Written by Prasanth Kumar Published on Updated on

SOQL IN Operator: Fix Too Many SOQL Queries Error | SalesforceTutorial

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

SOQL IN operator error: Too many SOQL queries 101 limit exception

“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.