Salesforce Object Query Language (SOQL) is a powerful tool that allows us to query and retrieve data from Salesforce. It is specifically designed to work with Salesforce’s relational database and is similar to SQL but with a focus on Salesforce’s data model.
As we delve into the intricacies of SOQL in salesforce, it’s important to understand not only how to write basic and advanced queries but also how to optimize them while keeping Salesforce’s governor limits in mind. In this lesson, we’ll walk through:

Table of Contents
1. Understanding SOQL Basics in Apex
Before we dive into advanced concepts, let’s build a solid foundation.
1.1 Basic SOQL Query Syntax
Every SOQL query starts with a SELECT
statement specifying the fields we wish to retrieve from an object. The basic structure looks like this:
SELECT field1, field2 FROM ObjectName
For instance, to fetch the Id
and Name
of all accounts, we write:
SELECT Id, Name FROM Account
1.2 Filtering Records Using the WHERE Clause
To retrieve specific records, we use the WHERE
clause. This clause lets us filter records based on criteria:
SELECT Id, Name FROM Account WHERE Industry = 'Technology'
This query returns only those accounts where the industry is “Technology.” It’s crucial to design our filters to be selective, reducing the amount of data processed and ensuring we stay within governor limits.
1.3 Sorting Data with ORDER BY
Sorting our query results can greatly enhance readability :
SELECT Name, Industry FROM Account ORDER BY Name ASC
By default, the data is sorted in ascending order (ASC
) using the Name field, but we can also use DESC
for descending order.
1.4 Leveraging the TYPEOF Clause
The TYPEOF
clause allows us to conditionally query different fields based on the record type of a polymorphic (has multiple types of data) field. This clause is particularly useful when dealing with relationships where the related object can be one of several types.
For instance, consider a scenario where we have a lookup field that could refer to multiple objects. Using TYPEOF
, we can query fields specific to the type of record returned:
SELECT Id,
TYPEOF What
WHEN Account THEN Name, Industry
WHEN Opportunity THEN Amount, StageName
END
FROM Event
In this query, the TYPEOF
clause allows us to retrieve Name
and Industry
when the What
field refers to an Account, and Amount
and StageName
when it refers to an Opportunity. This conditional logic helps us tailor our queries like a switch case based on the dynamic nature of the data.
2. Advanced SOQL Queries
As we progress, we’ll explore advanced SOQL features that empower us to retrieve meaningful insights when working with complex datasets.
2.1 Complex Filtering with Logical Operators
Often, our queries require multiple conditions. Logical operators like AND
, OR
, and LIKE
can help refine our data as follows:
SELECT Id, Name FROM Contact WHERE LastName LIKE 'S%' AND Email != NULL
This query retrieves contacts whose last names start with “S” and have an associated email address. Here % signifies that after ‘S’ we are accepting any alphabet in our LastName field.
2.2 Aggregating Data Queries with COUNT, SUM, AVG, and More
Aggregation functions allow us to analyze and summarize data. Let us look into the details:
COUNT: Returns the total number of records that match a condition.
SELECT COUNT(Id) FROM Opportunity WHERE StageName = 'Closed Won'
COUNT_DISTINCT(fieldName): Returns the number of distinct values for a given field.
SELECT COUNT_DISTINCT(Industry) FROM Account
SUM: Calculates the total sum of a numeric field.
SELECT SUM(Amount) FROM Opportunity WHERE StageName = 'Closed Won'
AVG: Computes the average value of a numeric field.
SELECT AVG(Amount) FROM Opportunity WHERE StageName = 'Prospecting'
MIN: Retrieves the minimum value of a specified field.
SELECT MIN(Amount) FROM Opportunity WHERE StageName = 'Closed Won'
MAX: Retrieves the maximum value of a specified field.
SELECT MAX(Amount) FROM Opportunity WHERE StageName = 'Closed Won'
GROUP BY: The GROUP BY
clause lets us aggregate data and group them by specific fields.
For example, to count the number of accounts per industry i.e. creating counts of accounts for each industry field, we can use:
SELECT Industry, COUNT(Id) FROM Account GROUP BY Industry

This query provides us a quick snapshot of data distribution. Using a GROUP BY
clause enables us to use the HAVING clause, returning us data based on the collective dataset rather than from the individual rows.
2.3 Advanced Grouping with ROLLUP and CUBE
GROUP BY ROLLUP
: This extension of the GROUP BY
clause aggregates query results creating subtotals at different levels, aggregating from fields (right to left). The order of rollup fields is important. For example, to group accounts by Industry and Type, and generate subtotals for each Industry, we can write:
SELECT Industry, Type, COUNT(Id) FROM Account GROUP BY ROLLUP(Industry, Type)

This query provides counts for each combination of Industry and Type, along with subtotal rows for each Industry and a grand total at the end.
GROUP BY ROLLUP (fieldName1, fieldName2...upto 3)
When using GROUP BY ROLLUP with fieldName1, fieldName2, and fieldName3, the query produces:
- First-level subtotals: Aggregates for each combination of fieldName1 and fieldName2, further grouped by fieldName3.
- Second-level subtotals: Aggregates for each distinct fieldName1, combining details from fieldName2 and fieldName3.
- Grand total: A single row summarizing the entire dataset.
GROUP BY CUBE
: This extension allows us to compute aggregations for all combinations of a set of fields. For example, to generate aggregates for all possible combinations of Industry and Type, we can use:
SELECT Industry, Type, COUNT(Id) FROM Account GROUP BY CUBE(Industry, Type)

This ‘every possible grouping of the specified fields’ can be particularly useful for multidimensional analysis.
These advanced grouping techniques enable us to produce rich, detailed reports that provide valuable insights into our data across multiple dimensions. Read more here.
3. Querying Parent-Child Relationships
One of SOQL’s most powerful features is its ability to traverse relationships between objects. This allows us to efficiently fetch related data.
3.1 Querying Parent Objects (Child-to-Parent Query)
When we need data from a parent object, we use dot notation:
SELECT Id, Name, Account.Name FROM Contact
This query returns each contact’s name along with the name of the related account, providing a seamless way to navigate parent-child relationships.

These can go up to 5 levels of parents and grandparents, great grandparent, and so on. Point to note, the custom relationships always ends with __r.
3.2 Querying Child Objects (Parent-to-Child Query)
To retrieve related child records, we use subqueries:
SELECT Id, Name, (SELECT Id, LastName FROM Contacts) FROM Account

Here, for each account, we also fetch its associated contacts. This nested approach helps us pull in comprehensive datasets with a single query. These queries can only go down one level but not to child of child records.
4. Handling Query Limits and Optimizing SOQL
Salesforce imposes strict governor limits to ensure system performance and fairness across all users. Understanding and working within these limits is crucial for efficient query design.
4.1 Understanding Governor Limits
Some key governor limits we need to be aware of include:
- 50,000 records per transaction: We must design queries to avoid returning an excessive number of records.
- 100 synchronous and 200 asynchronous SOQL queries per transaction: This requires us to avoid writing queries inside loops and to batch operations where possible.
By keeping these limits in mind, we ensure our applications remain performant and reliable.
4.2 Best Practices for Optimizing Queries
- Use Selective Filters: Ensure that our
WHERE
clauses are selective enough to reduce unnecessary data scans. Using indexed fields in filters can dramatically improve performance. - Leverage the LIMIT Clause: When possible, restrict the number of records returned by the query.
SELECT Id, Name FROM Account WHERE Industry = 'Banking' LIMIT 10
- Avoid SOQL Queries in Loops: Executing a query inside a loop can quickly lead to hitting governor limits. Instead, fetch all necessary data in one query and process it in memory.
- Utilize the Query Plan Tool: This tool can help us understand how our queries are executed and whether they use indexes effectively. It’s a valuable asset for optimizing queries.
- Consider Data Skew: Be mindful of queries on objects with large numbers of records. Proper filtering and using selective indexes are key strategies to manage this.
- Batch Processing: For operations that require handling large data volumes, consider using batch processing techniques to work within the limits.
Implementing these strategies not only prevents errors like “Too many query rows” but also ensures that our queries are robust and scalable.
Final Thoughts
SOQL in Apex is a critical tool for retrieving and managing Salesforce data. By mastering both basic and advanced queries, and by applying optimization best practices, we can write efficient code that works seamlessly within Salesforce’s governor limits.
As we continue to develop our skills, it’s important to experiment with these techniques in real-world scenarios. Let’s keep refining our queries, monitor performance with tools like the Query Plan, and always design our code with scalability in mind.
Happy querying, and let’s keep pushing the boundaries of what we can achieve with Salesforce! 🚀