Salesforce SOQL: Salesforce Object Query Language
Every application needs a way to ask its database a question: "Give me all customers in Mumbai with revenue above ₹1 crore." In Salesforce, that question is written in SOQL — Salesforce Object Query Language. SOQL is how Apex code retrieves records from the Salesforce database. Every developer and advanced administrator needs to understand it thoroughly.
What Is SOQL?
SOQL is a query language that looks similar to SQL (Structured Query Language) used in traditional databases. The main difference is that SOQL is designed specifically for Salesforce's object model — so instead of querying tables, you query Salesforce objects. SOQL can only retrieve data — it cannot insert, update, or delete records. For those operations, you use DML statements.
The Library Search Analogy
QUESTION: "Find me all science books published after 2010
that are currently available, sorted by title."
SOQL EQUIVALENT:
SELECT Title, Author, Year
FROM Book__c
WHERE Category = 'Science'
AND Year > 2010
AND IsAvailable__c = true
ORDER BY Title ASC
Basic SOQL Syntax
Every SOQL query follows this structure:
SELECT field1, field2, field3 FROM ObjectAPIName WHERE condition ORDER BY fieldName ASC|DESC LIMIT numberOfRecords
A Real SOQL Example
SELECT Id, Name, Industry, AnnualRevenue, Phone FROM Account WHERE Industry = 'Technology' AND AnnualRevenue > 10000000 ORDER BY AnnualRevenue DESC LIMIT 10
This query returns the top 10 Technology accounts with annual revenue above ₹1 crore, sorted from highest to lowest revenue. Each clause plays a specific role:
- SELECT — lists which fields to return
- FROM — names the object to query
- WHERE — filters which records to include
- ORDER BY — sorts the results
- LIMIT — caps the number of results returned
Using SOQL in Apex
In Apex, you embed SOQL queries inside square brackets. The result is stored in a List or a single record variable.
// Return multiple records into a List
List<Account> techAccounts = [
SELECT Id, Name, AnnualRevenue
FROM Account
WHERE Industry = 'Technology'
AND AnnualRevenue > 10000000
ORDER BY AnnualRevenue DESC
LIMIT 10
];
// Loop through results
for (Account acc : techAccounts) {
System.debug('Account: ' + acc.Name + ' | Revenue: ' + acc.AnnualRevenue);
}
// Return a single record
Account topAccount = [
SELECT Id, Name, AnnualRevenue
FROM Account
ORDER BY AnnualRevenue DESC
LIMIT 1
];
System.debug('Top Account: ' + topAccount.Name);
WHERE Clause Operators
| Operator | Meaning | Example |
|---|---|---|
| = | Equals | Industry = 'Healthcare' |
| != | Not equal to | Status != 'Closed' |
| > | Greater than | Amount > 500000 |
| < | Less than | NumberOfEmployees < 50 |
| >= | Greater than or equal to | CloseDate >= TODAY() |
| LIKE | Pattern match (% is wildcard) | Name LIKE 'Tata%' |
| IN | Matches any value in a list | Industry IN ('IT', 'Finance') |
| NOT IN | Does not match any value in a list | StageName NOT IN ('Closed Won', 'Closed Lost') |
| INCLUDES | Multi-select picklist contains value | Skills__c INCLUDES ('Java') |
Date Functions in SOQL
SOQL provides built-in date literals that make filtering by relative dates very easy:
-- Opportunities closing this month SELECT Id, Name, CloseDate FROM Opportunity WHERE CloseDate = THIS_MONTH -- Cases created in the last 7 days SELECT Id, Subject FROM Case WHERE CreatedDate = LAST_N_DAYS:7 -- Accounts modified this year SELECT Id, Name FROM Account WHERE LastModifiedDate = THIS_YEAR
Date literals available include: TODAY, YESTERDAY, THIS_WEEK, LAST_WEEK, THIS_MONTH, LAST_MONTH, THIS_QUARTER, THIS_YEAR, LAST_N_DAYS:n, NEXT_N_DAYS:n, and more.
Relationship Queries
Because Salesforce objects are related, SOQL lets you query across those relationships in two directions.
Child to Parent (Dot Notation)
Access a parent record's fields from a child query using the relationship name and a dot:
SELECT Id, Name, Account.Name, Account.Industry FROM Contact WHERE Account.Industry = 'Finance'
This retrieves Contact records and also shows the Account name and industry for each Contact's parent Account.
Parent to Child (Subquery)
Retrieve a parent record and include its related child records in the same query:
SELECT Id, Name,
(SELECT Id, Subject, Status FROM Cases)
FROM Account
WHERE Name = 'Infosys'
This returns the Infosys Account and, nested inside it, all Cases related to that Account. In Apex, you access the child records through the relationship name: account.Cases.
Aggregate Functions
SOQL supports aggregate functions for summarizing data — similar to Excel formulas like SUM and COUNT:
-- Count all open Opportunities SELECT COUNT() FROM Opportunity WHERE IsClosed = false -- Total value of open Opportunities by Stage SELECT StageName, SUM(Amount) totalAmount, COUNT(Id) dealCount FROM Opportunity WHERE IsClosed = false GROUP BY StageName ORDER BY totalAmount DESC
Available aggregate functions: COUNT(), COUNT(fieldName), SUM(), AVG(), MIN(), MAX(). When you use aggregate functions with GROUP BY, results return as AggregateResult objects in Apex.
SOQL Governor Limits
Salesforce enforces limits on SOQL to protect shared server resources:
- 100 SOQL queries per synchronous transaction
- 200 SOQL queries per asynchronous transaction (batch, future, queueable)
- 50,000 records maximum returned by a single query
The most critical rule: never put a SOQL query inside a loop. If you loop through 200 records and run a SOQL inside the loop, Salesforce executes 200 queries — blowing past the 100-query limit and throwing an error.
// BAD - SOQL inside loop (will fail at scale)
for (Account acc : accounts) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}
// GOOD - One SOQL outside loop
List<Contact> allContacts = [SELECT Id, AccountId FROM Contact
WHERE AccountId IN :accountIds];
Key Points
- SOQL retrieves records from the Salesforce database — it reads data only, not modifies it.
- Every query uses SELECT (fields), FROM (object), WHERE (filter), ORDER BY (sort), and LIMIT (cap).
- Relationship queries let you traverse parent-to-child and child-to-parent connections in one query.
- Aggregate functions like COUNT(), SUM(), and AVG() summarize data using GROUP BY.
- Never put SOQL queries inside loops — collect IDs first and use a single IN query outside the loop.
