LLMs.txt Best Salesforce SOQL-20 Queries Every Developer Must Know

Salesforce SOQL Tutorial — 20 Queries Every Developer Must Know

About RizeX Labs (formerly Gradx Academy): RizeX Labs (formerly Gradx Academy) is your trusted source for valuable information and resources. We provide reliable, well-researched information content to keep you informed and help you make better decisions. This content focuses on Salesforce SOQL Tutorial — 20 Queries Every Developer Must Know and related topics.

Table of Contents

Introduction: Why SOQL Is the Most Important Skill for Salesforce Developers

Every piece of data in Salesforce lives in objects. Accounts, Contacts, Opportunities, Cases, custom objects — they’re all database tables under the hood. And the only way to read that data programmatically is through SOQL.

SOQL (Salesforce Object Query Language) is Salesforce’s proprietary query language for retrieving records from the Salesforce database. If you’re writing Apex code, building integrations, debugging data issues, or working in the Developer Console — you are using SOQL. There is no way around it.

Here’s why SOQL is critical for every Salesforce developer:

Descriptive alt text for image 2 - This image shows important visual content that enhances the user experience and provides context for the surrounding text.
  • Apex triggers and classes use SOQL to fetch records before processing them
  • Reports and dashboards are powered by query logic under the surface
  • REST and SOAP APIs accept SOQL queries to return specific datasets
  • Data Loader and Workbench use SOQL for targeted data exports
  • Flow Builder generates SOQL behind the scenes in Get Records elements

In short: if Apex is the engine of Salesforce development, SOQL is the fuel.

This Salesforce SOQL tutorial covers everything — from basic syntax to advanced relationship queries, aggregate functions, dynamic SOQL, and the 20 must-know queries you’ll use in real projects and interviews. Whether you’re just starting your Salesforce tutorial journey or preparing for a developer interview, this guide gives you practical, immediately applicable knowledge.

Let’s build your SOQL expertise from the ground up.


Section 1: What Is SOQL in Salesforce?

SOQL (Salesforce Object Query Language) is a query language designed specifically for the Salesforce platform. It allows you to search Salesforce records, retrieve specific fields, apply filters, sort results, and traverse object relationships.

SOQL is read-only — it only retrieves data. To create, update, or delete records, you use DML (Data Manipulation Language) statements in Apex.

Where Is SOQL Used?

ContextHow SOQL Is Used
Apex CodeInline queries to fetch records for processing
Developer ConsoleQuery Editor tab for ad-hoc data exploration
Salesforce CLIsf data query command for terminal-based queries
WorkbenchSOQL query interface for testing and data export
REST API/services/data/vXX.0/query?q=SELECT... endpoint
SOAP APIquery() and queryMore() calls
Flow BuilderBehind the scenes in Get Records elements

What Makes SOQL Different From Standard Database Queries?

SOQL is purpose-built for Salesforce’s multi-tenant architecture. Unlike a traditional database where you have direct table access, Salesforce:

  • Enforces sharing rules and field-level security automatically
  • Applies governor limits to prevent one org from consuming too many resources
  • Provides relationship traversal syntax for parent-child object navigation
  • Includes Salesforce-specific date literals like TODAYLAST_N_DAYS:30
  • Returns sObjects (Salesforce objects) rather than generic rows

Section 2: SOQL vs SQL — Key Differences

This is one of the most common topics in Salesforce interviews. If you already know SQL from another platform, understanding these differences is critical before you write your first Salesforce query language statement.

Comparison Table

FeatureSQLSOQL
PurposeGeneral-purpose relational DB queriesSalesforce-specific data retrieval
SELECT *✅ Supported❌ Not supported
JOINs✅ Full JOIN support (INNER, LEFT, etc.)❌ No JOINs — uses relationship queries
INSERT/UPDATE/DELETE✅ Full DML in queries❌ Read-only — DML done separately in Apex
Multiple tables✅ Query multiple tables in one statement⚠️ Only one primary object, related via subquery
Wildcards in SELECT✅ SELECT *❌ Must specify field names
Date literalsLimited (database-specific)✅ Rich date literals (TODAY, LAST_WEEK, etc.)
Aggregate functionsCOUNT, SUM, AVG, MIN, MAXCOUNT, COUNT_DISTINCT, SUM, AVG, MIN, MAX
HAVING clause✅ Supported✅ Supported (with GROUP BY)
OFFSET✅ Supported✅ Supported (max 2000)
Subqueries✅ Full subquery support⚠️ Limited — only on child relationships
Sharing rules enforced❌ No✅ Yes (unless WITH SECURITY_ENFORCED)
Record limitDatabase-dependent50,000 records per query (synchronous)
Null comparisonIS NULL / IS NOT NULL= null / != null
Descriptive alt text for image 3 - This image shows important visual content that enhances the user experience and provides context for the surrounding text.

SOQL Limitations to Know

  • ❌ Cannot use SELECT * — you must name every field
  • ❌ Cannot query two unrelated objects in a single SOQL statement
  • ❌ Cannot perform DML (INSERT, UPDATE, DELETE) within SOQL
  • ❌ Maximum of 50,000 records returned in a synchronous query
  • ❌ Maximum 100 SOQL queries per Apex transaction (governor limit)
  • ❌ Subqueries only work on child relationships, not arbitrary joins

SOQL Advantages Over Generic SQL

  • ✅ Automatic security enforcement — sharing rules and FLS applied by default
  • ✅ Rich date literals — LAST_QUARTERNEXT_N_DAYS:30THIS_FISCAL_YEAR
  • ✅ Relationship traversal — navigate parent-child hierarchies without JOINs
  • ✅ Salesforce-specific functions — toLabel()convertCurrency()FORMAT()
  • ✅ Semi-joins and anti-joins — powerful filtering using EXISTS-style patterns

Section 3: Basic SOQL Syntax

Every SOQL query follows this fundamental structure:

SQLSELECT field1, field2, field3
FROM ObjectAPIName
WHERE condition
ORDER BY field ASC|DESC
LIMIT n

Clause Breakdown

ClauseRequired?Purpose
SELECT✅ YesSpecifies which fields to return
FROM✅ YesSpecifies the object to query
WHERE❌ OptionalFilters records by condition
ORDER BY❌ OptionalSorts results
LIMIT❌ OptionalRestricts number of records returned
OFFSET❌ OptionalSkips a number of records (for pagination)
GROUP BY❌ OptionalGroups results for aggregate queries
HAVING❌ OptionalFilters aggregate results (used with GROUP BY)

Your First SOQL Query

SQLSELECT Id, Name, Industry, AnnualRevenue
FROM Account
WHERE Industry = 'Technology'
ORDER BY AnnualRevenue DESC
LIMIT 10

What this does: Retrieves the Id, Name, Industry, and Annual Revenue of the top 10 Technology accounts, sorted by revenue from highest to lowest.

Important Syntax Rules

  • Field and object names are case-insensitive in SOQL (Account = account)
  • String values in WHERE clauses are case-insensitive by default ('Salesforce' = 'salesforce')
  • Always use the API name, not the label (e.g., Annual_Revenue__c, not “Annual Revenue”)
  • Custom fields always end in __c, custom objects always end in __c
  • Custom relationship fields end in __r

Section 4: 20 Must-Know SOQL Examples

This is the core of the guide. Every query here is based on real scenarios you’ll encounter in production orgs, Apex development, and interviews.


Query 1: Fetch Basic Account Records

Scenario: Get the name, phone, and website of all accounts in the org.

SQLSELECT Id, Name, Phone, Website, Industry
FROM Account
LIMIT 100

Explanation:

  • Always include Id — you’ll need it for DML operations later
  • LIMIT 100 is a safety net; without it you could retrieve thousands of records
  • In Apex, omitting LIMIT in large orgs will hit the 50,000 record governor limit

Query 2: Filter Records with WHERE Clause

Scenario: Find all Contacts who work at companies in the Finance industry.

SQLSELECT Id, FirstName, LastName, Email, AccountId
FROM Contact
WHERE Account.Industry = 'Finance'
AND Email != null

Explanation:

  • Account.Industry traverses the relationship from Contact to its parent Account (child-to-parent)
  • Email != null filters out contacts without email addresses
  • Both conditions must be true (AND logic)

Query 3: AND / OR Conditions

Scenario: Find Opportunities that are either in “Proposal” stage OR have an amount greater than $500,000.

SQLSELECT Id, Name, StageName, Amount, CloseDate, OwnerId
FROM Opportunity
WHERE StageName = 'Proposal/Price Quote'
OR Amount > 500000
ORDER BY Amount DESC

Explanation:

  • Records matching either condition are returned
  • Use parentheses to control complex AND/OR logic:
SQL-- More complex example with parentheses
SELECT Id, Name, StageName, Amount
FROM Opportunity
WHERE (StageName = 'Proposal/Price Quote' OR StageName = 'Negotiation/Review')
AND Amount > 100000
AND IsClosed = false

Query 4: ORDER BY and LIMIT

Scenario: Find the 5 most recently created Cases that are still open.

SQLSELECT Id, CaseNumber, Subject, Status, Priority, CreatedDate
FROM Case
WHERE IsClosed = false
ORDER BY CreatedDate DESC
LIMIT 5

Explanation:

  • ORDER BY CreatedDate DESC sorts newest first
  • Use ASC for oldest first (default if ORDER BY is omitted)
  • LIMIT 5 ensures we only get the top 5
  • This exact pattern is used in dashboards showing “latest open cases”

Query 5: COUNT — Aggregate Function

Scenario: How many Leads were created in the system from the “Web” source?

SQLSELECT COUNT(Id)
FROM Lead
WHERE LeadSource = 'Web'
AND IsConverted = false

Explanation:

  • COUNT(Id) returns the number of records matching the filter
  • In Apex, access the result like this:
apexInteger webLeadCount = [
    SELECT COUNT(Id) 
    FROM Lead 
    WHERE LeadSource = 'Web' 
    AND IsConverted = false
].size();

// Or using AggregateResult:
AggregateResult[] result = [
    SELECT COUNT(Id) cnt 
    FROM Lead 
    WHERE LeadSource = 'Web'
];
Integer count = (Integer) result[0].get('cnt');

Query 6: SUM and AVG Aggregate Functions

Scenario: What is the total value and average deal size of all Closed Won opportunities this year?

SQLSELECT 
    SUM(Amount) totalRevenue,
    AVG(Amount) averageDeal,
    COUNT(Id) totalDeals,
    MIN(Amount) smallestDeal,
    MAX(Amount) largestDeal
FROM Opportunity
WHERE StageName = 'Closed Won'
AND CloseDate = THIS_YEAR

Explanation:

  • Aliases (totalRevenueaverageDeal) are required when using aggregate functions alongside others
  • THIS_YEAR is a SOQL date literal — no need to hardcode year values
  • In Apex, these results come back as AggregateResult objects:
apexAggregateResult[] results = [
    SELECT SUM(Amount) totalRevenue, AVG(Amount) averageDeal
    FROM Opportunity
    WHERE StageName = 'Closed Won'
    AND CloseDate = THIS_YEAR
];

Decimal total = (Decimal) results[0].get('totalRevenue');
Decimal average = (Decimal) results[0].get('averageDeal');

Query 7: GROUP BY

Scenario: Show the total number of Opportunities and total amount, grouped by Stage.

SQLSELECT StageName, COUNT(Id) oppCount, SUM(Amount) totalAmount
FROM Opportunity
WHERE IsClosed = false
GROUP BY StageName
ORDER BY SUM(Amount) DESC

Explanation:

  • GROUP BY collapses multiple records into one row per unique value
  • Every field in SELECT must either be in GROUP BY or wrapped in an aggregate function
  • ORDER BY can use aggregate expressions directly

Query 8: GROUP BY with HAVING

Scenario: Find Lead sources that have generated more than 50 leads.

SQLSELECT LeadSource, COUNT(Id) leadCount
FROM Lead
GROUP BY LeadSource
HAVING COUNT(Id) > 50
ORDER BY COUNT(Id) DESC

Explanation:

  • HAVING filters on aggregate results — it’s the WHERE clause for GROUP BY
  • You cannot use WHERE COUNT(Id) > 50 — that’s a SQL misconception that breaks SOQL
  • This query is useful for marketing analytics and source attribution reporting

Query 9: Child-to-Parent Relationship Query

Scenario: Get Contact names along with their parent Account’s name, industry, and phone number.

SQLSELECT 
    Id, 
    FirstName, 
    LastName, 
    Email,
    Account.Name,
    Account.Industry,
    Account.Phone,
    Account.BillingCity
FROM Contact
WHERE Account.Industry = 'Technology'
AND Account.BillingCity = 'San Francisco'

Explanation:

  • Use dot notation (Account.Name) to traverse the parent relationship
  • For standard relationships: use the relationship name (e.g., AccountOwnerCreatedBy)
  • For custom relationships: use the relationship API name ending in __r (e.g., Custom_Object__r.Field__c)
  • You can traverse up to 5 levels of parent relationships in one query
SQL-- Custom object example
SELECT 
    Id, 
    Name,
    Project__r.Name,
    Project__r.Account__r.Name
FROM Task__c
WHERE Project__r.Status__c = 'Active'

Query 10: Parent-to-Child Relationship Query (Subquery)

Scenario: Fetch all Accounts and their associated open Opportunities in a single query.

SQLSELECT 
    Id, 
    Name, 
    Industry,
    (
        SELECT Id, Name, StageName, Amount, CloseDate
        FROM Opportunities
        WHERE IsClosed = false
        ORDER BY Amount DESC
    )
FROM Account
WHERE Industry = 'Technology'
AND IsActive__c = true

Explanation:

  • The inner SELECT inside parentheses is a subquery (also called a nested query)
  • Use the relationship name (plural for standard: OpportunitiesContactsCases)
  • For custom child relationships, use the child relationship name defined on the lookup field
  • In Apex, access child records like this:
apexList<Account> accounts = [
    SELECT Id, Name,
        (SELECT Id, Name, Amount FROM Opportunities WHERE IsClosed = false)
    FROM Account
    WHERE Industry = 'Technology'
];

for (Account acc : accounts) {
    List<Opportunity> childOpps = acc.Opportunities;
    System.debug('Account: ' + acc.Name + ' has ' + childOpps.size() + ' open opps');
}

Query 11: LIKE Operator for Pattern Matching

Scenario: Find all Contacts whose last name starts with “Smith” or whose email contains “gmail.”

SQL-- Pattern: Last name starts with 'Smith'
SELECT Id, FirstName, LastName, Email
FROM Contact
WHERE LastName LIKE 'Smith%'

-- Pattern: Email contains 'gmail'
SELECT Id, FirstName, LastName, Email
FROM Contact
WHERE Email LIKE '%gmail%'

-- Pattern: Exactly 5 characters (use underscores)
SELECT Id, Name
FROM Account
WHERE Name LIKE '_____'

Explanation:

  • % matches any sequence of characters (zero or more)
  • _ matches exactly one character
  • LIKE is case-insensitive in SOQL
  • Combine multiple LIKE patterns with AND/OR

Query 12: Date Literals and Date Filters

Scenario: Find all Opportunities closing in the next 30 days, and all Leads created last week.

SQL-- Opportunities closing in next 30 days
SELECT Id, Name, StageName, Amount, CloseDate
FROM Opportunity
WHERE CloseDate = NEXT_N_DAYS:30
AND IsClosed = false
ORDER BY CloseDate ASC

-- Leads created last week
SELECT Id, FirstName, LastName, LeadSource, CreatedDate
FROM Lead
WHERE CreatedDate = LAST_WEEK
AND IsConverted = false

Complete Date Literals Reference:

Date LiteralWhat It Represents
TODAYCurrent day (midnight to midnight)
YESTERDAYPrevious day
TOMORROWNext day
THIS_WEEKSunday to Saturday of current week
LAST_WEEKPrevious Sunday to Saturday
NEXT_WEEKNext Sunday to Saturday
THIS_MONTHFirst to last day of current month
LAST_MONTHPrevious calendar month
THIS_QUARTERCurrent fiscal/calendar quarter
THIS_YEARJanuary 1 to December 31 of current year
LAST_N_DAYS:nLast n days including today
NEXT_N_DAYS:nNext n days starting tomorrow
LAST_N_MONTHS:nLast n calendar months
NEXT_N_WEEKS:nNext n weeks
THIS_FISCAL_YEARCurrent fiscal year (org-configured)

Query 13: NULL Handling

Scenario: Find all Contacts with no email address, and all Accounts with no assigned owner.

SQL-- Contacts missing email
SELECT Id, FirstName, LastName, AccountId
FROM Contact
WHERE Email = null
ORDER BY LastName ASC

-- Accounts with a description (NOT null)
SELECT Id, Name, Description, Industry
FROM Account
WHERE Description != null
AND Industry != null

Explanation:

  • SOQL uses = null and != null (NOT IS NULL / IS NOT NULL like SQL)
  • Null checks are essential for data quality automations and cleanup scripts
  • Combine null checks with other conditions to build targeted cleanup queries

Query 14: IN Clause

Scenario: Find all Contacts who work at a specific set of named accounts.

SQL-- Static IN clause
SELECT Id, FirstName, LastName, Email, AccountId
FROM Contact
WHERE Account.Name IN ('Salesforce', 'Google', 'Microsoft', 'Amazon', 'Apple')

-- Dynamic IN clause from a Set in Apex
apex// In Apex — dynamic IN clause
Set<String> targetIndustries = new Set<String>{'Technology', 'Finance', 'Healthcare'};

List<Account> accounts = [
    SELECT Id, Name, Industry, AnnualRevenue
    FROM Account
    WHERE Industry IN :targetIndustries
    AND AnnualRevenue > 1000000
    ORDER BY AnnualRevenue DESC
];

Explanation:

  • IN accepts a comma-separated list of values or (in Apex) a bound Set/List variable
  • The :variableName syntax in Apex is called variable binding — it’s secure and prevents SOQL injection
  • NOT IN works the same way to exclude records

Query 15: OFFSET for Pagination

Scenario: Build a paginated list of Accounts, showing records 21–40 (page 2 with 20 records per page).

SQL-- Page 1: Records 1-20
SELECT Id, Name, Industry, AnnualRevenue
FROM Account
WHERE IsActive__c = true
ORDER BY Name ASC
LIMIT 20
OFFSET 0

-- Page 2: Records 21-40
SELECT Id, Name, Industry, AnnualRevenue
FROM Account
WHERE IsActive__c = true
ORDER BY Name ASC
LIMIT 20
OFFSET 20

-- Page 3: Records 41-60
SELECT Id, Name, Industry, AnnualRevenue
FROM Account
WHERE IsActive__c = true
ORDER BY Name ASC
LIMIT 20
OFFSET 40

Explanation:

  • OFFSET skips the specified number of records before returning results
  • Maximum OFFSET value is 2,000 — for larger datasets, use cursor-based pagination
  • Always use ORDER BY with OFFSET — without it, the record order is unpredictable and your pagination will be unreliable
  • In Apex: OFFSET (pageNumber - 1) * pageSize

Query 16: Semi-Join (Filter Using a Subquery)

Scenario: Find all Contacts who are linked to Accounts that have at least one Closed Won Opportunity.

SQLSELECT Id, FirstName, LastName, Email, AccountId
FROM Contact
WHERE AccountId IN (
    SELECT AccountId
    FROM Opportunity
    WHERE StageName = 'Closed Won'
    AND CloseDate = THIS_YEAR
)
ORDER BY LastName ASC

Explanation:

  • semi-join filters the outer query based on the results of an inner query
  • The inner query must return a single field that matches the outer query’s filter field
  • Both fields must be ID fields (or the same type)
  • Semi-joins are powerful for targeted list building without complex Apex logic

Query 17: Anti-Join (Exclude Based on Subquery)

Scenario: Find all Accounts that have never had an Opportunity created.

SQLSELECT Id, Name, Industry, CreatedDate, OwnerId
FROM Account
WHERE Id NOT IN (
    SELECT AccountId
    FROM Opportunity
    WHERE AccountId != null
)
ORDER BY CreatedDate DESC

Explanation:

  • An anti-join uses NOT IN with a subquery to find records that don’t match the inner query
  • This is the SOQL equivalent of a SQL LEFT JOIN ... WHERE right_table.id IS NULL
  • Critical for data quality: finding orphan records, uncovered accounts, leads never followed up

Query 18: Query on Custom Objects and Fields

Scenario: Query a custom object Project__c to find active projects with a budget over $100,000.

SQLSELECT 
    Id,
    Name,
    Project_Status__c,
    Budget__c,
    Start_Date__c,
    End_Date__c,
    Project_Manager__r.Name,
    Project_Manager__r.Email,
    Account__r.Name,
    Account__r.Industry,
    (
        SELECT Id, Name, Status__c, Due_Date__c
        FROM Tasks__r
        WHERE Status__c != 'Completed'
        ORDER BY Due_Date__c ASC
    )
FROM Project__c
WHERE Project_Status__c = 'Active'
AND Budget__c > 100000
AND Start_Date__c <= TODAY
AND End_Date__c >= TODAY
ORDER BY Budget__c DESC

Explanation:

  • Custom object API names end in __c (e.g., Project__c)
  • Custom field API names end in __c (e.g., Budget__cProject_Status__c)
  • Custom lookup relationships use __r for traversal (e.g., Project_Manager__r.Name)
  • Custom child relationship names end in __r (defined on the lookup field’s “Child Relationship Name”)
  • This query demonstrates a complete real-world pattern: filtering by date, status, and amount while joining parent and child records

Query 19: FOR UPDATE (Record Locking)

Scenario: You need to read Account records and update them in Apex, ensuring no other process modifies them simultaneously.

SQLSELECT Id, Name, AnnualRevenue, Rating
FROM Account
WHERE Industry = 'Technology'
AND Rating = 'Cold'
LIMIT 50
FOR UPDATE

In Apex context:

apexList<Account> accountsToProcess = [
    SELECT Id, Name, AnnualRevenue, Rating
    FROM Account
    WHERE Industry = 'Technology'
    AND Rating = 'Cold'
    LIMIT 50
    FOR UPDATE
];

// These records are now locked — no other process can update them
// until this transaction completes

List<Account> toUpdate = new List<Account>();

for (Account acc : accountsToProcess) {
    if (acc.AnnualRevenue >= 1000000) {
        acc.Rating = 'Hot';
        toUpdate.add(acc);
    }
}

if (!toUpdate.isEmpty()) {
    update toUpdate;
}
// Lock is released when the transaction ends

Explanation:

  • FOR UPDATE locks the queried records so no other process can modify them during your transaction
  • Essential in concurrent environments where multiple processes might update the same records
  • Records are automatically unlocked when the transaction completes (success or failure)
  • Cannot be used in Visualforce controllers or read-only contexts

Query 20: Dynamic SOQL

Scenario: Build a flexible search feature where the object name, fields, and filter conditions are determined at runtime.

apexpublic class DynamicSOQLExample {
    
    public static List<SObject> searchRecords(
        String objectName, 
        String searchField, 
        String searchValue,
        Integer recordLimit
    ) {
        // Build query string dynamically
        String query = 'SELECT Id, Name, CreatedDate ' +
                       'FROM ' + objectName + ' ' +
                       'WHERE ' + searchField + ' LIKE :searchPattern ' +
                       'ORDER BY CreatedDate DESC ' +
                       'LIMIT :lim';
        
        // Use variable binding for security (prevents SOQL injection)
        String searchPattern = '%' + searchValue + '%';
        Integer lim = (recordLimit != null && recordLimit > 0) ? 
                       recordLimit : 50;
        
        // Execute dynamic query
        return Database.query(query);
    }
    
    // ⚠️ NEVER do this — SOQL Injection vulnerability
    public static List<SObject> unsafeQuery(String userInput) {
        // DANGEROUS: User input directly in query string
        String badQuery = 'SELECT Id, Name FROM Account WHERE Name = \'' + 
                          userInput + '\'';
        return Database.query(badQuery);
    }
    
    // ✅ SAFE Dynamic SOQL with String.escapeSingleQuotes()
    public static List<SObject> safeQuery(String userInput) {
        // Sanitize user input first
        String safeInput = String.escapeSingleQuotes(userInput);
        String goodQuery = 'SELECT Id, Name, Industry FROM Account ' + 
                           'WHERE Name = \'' + safeInput + '\'';
        return Database.query(goodQuery);
    }
}

Explanation:

  • Database.query(queryString) executes a dynamic SOQL query at runtime
  • Use variable binding (:variableName) wherever possible in dynamic queries
  • When you must embed user input as a string, always use String.escapeSingleQuotes()
  • Dynamic SOQL enables building generic search components, configurable automation, and flexible reporting tools
  • Cannot use variable binding for object names, field names, or operators — only for values

Section 5: SOQL in Apex — Practical Usage

Inline SOQL (Static SOQL)

Inline SOQL is written directly inside Apex code, enclosed in square brackets.

apex// Single record — use List to avoid exceptions if no record found
List<Account> accounts = [
    SELECT Id, Name, Industry, AnnualRevenue
    FROM Account
    WHERE Name = 'Salesforce'
    LIMIT 1
];

if (!accounts.isEmpty()) {
    Account salesforceAccount = accounts[0];
    System.debug('Found: ' + salesforceAccount.Name);
}

// Multiple records in a list
List<Opportunity> openOpps = [
    SELECT Id, Name, StageName, Amount, CloseDate
    FROM Opportunity
    WHERE IsClosed = false
    AND OwnerId = :UserInfo.getUserId()
    ORDER BY CloseDate ASC
];

// Aggregate query
AggregateResult[] stageGroups = [
    SELECT StageName, COUNT(Id) cnt, SUM(Amount) total
    FROM Opportunity
    WHERE IsClosed = false
    GROUP BY StageName
];

for (AggregateResult ar : stageGroups) {
    String stage = (String) ar.get('StageName');
    Integer count = (Integer) ar.get('cnt');
    Decimal total = (Decimal) ar.get('total');
    System.debug(stage + ': ' + count + ' deals worth $' + total);
}

Dynamic SOQL

Dynamic SOQL uses Database.query() with a string-based query.

apex// Use when the object or fields are determined at runtime
public static List<SObject> getRecordsByStatus(
    String objectAPI, 
    String statusField, 
    String statusValue
) {
    String safeStatus = String.escapeSingleQuotes(statusValue);
    String safeField  = String.escapeSingleQuotes(statusField);
    
    String query = 'SELECT Id, Name, CreatedDate, LastModifiedDate ' +
                   'FROM '  + objectAPI + ' ' +
                   'WHERE ' + safeField + ' = \'' + safeStatus + '\' ' +
                   'ORDER BY CreatedDate DESC ' +
                   'LIMIT 200';
    
    return Database.query(query);
}

When to Use Each

SituationUse Inline SOQLUse Dynamic SOQL
Object and fields known at design time
Object or fields determined at runtime
Maximum compile-time safety needed
Building a generic search/filter component
Configurable automation (CMT-driven)
User input directly in filter✅ (with sanitization)

Default choice: Always prefer inline SOQL. Use dynamic SOQL only when runtime flexibility is genuinely required.


Section 6: SOQL Best Practices

These aren’t theoretical suggestions — they’re the difference between code that works in a demo and code that survives production.

1. Never Use SELECT * Thinking

SOQL doesn’t allow SELECT *, and that’s a feature, not a limitation. Always select only the fields you need.

apex// ❌ BAD — querying unnecessary fields wastes heap memory
List<Account> accs = [SELECT Id, Name, Industry, AnnualRevenue, 
                       BillingCity, BillingState, BillingCountry,
                       Phone, Website, Description, NumberOfEmployees,
                       // ... 30 more fields you'll never use
                       FROM Account];

// ✅ GOOD — query only what you need
List<Account> accs = [SELECT Id, Name, Industry FROM Account];

Why it matters: Every unnecessary field increases heap memory consumption. With 50,000 records, fetching 40 unnecessary fields can hit the 12MB heap limit.

2. Write Selective Queries

A query is selective when its WHERE clause filters out a large percentage of records. Salesforce uses indexes on specific fields to make selective queries fast.

Always-indexed fields:

  • Id
  • Name
  • CreatedDate
  • SystemModStamp
  • OwnerId
  • Fields marked as External ID
  • Fields marked as Unique
SQL-- ✅ Selective — uses indexed Id field
SELECT Id, Name FROM Account WHERE Id IN :accountIds

-- ✅ Selective — uses indexed OwnerId
SELECT Id, Name FROM Opportunity WHERE OwnerId = :userId

-- ❌ Non-selective — Description is not indexed, full table scan
SELECT Id, Name FROM Account WHERE Description LIKE '%cloud%'

3. Respect Governor Limits

LimitValueHow to Avoid Hitting It
SOQL queries per transaction100Batch queries, avoid SOQL in loops
Records returned per query50,000Use LIMIT, pagination, SOQL for loops
Aggregate query rows50,000Filter aggressively with WHERE
Total heap size6MB (sync) / 12MB (async)Select fewer fields, process in batches
apex// ✅ SOQL for loop — processes records in chunks, avoids heap limit
for (Account acc : [SELECT Id, Name, AnnualRevenue FROM Account WHERE Industry = 'Finance']) {
    // Process each record without loading all into memory simultaneously
}

4. Use Variable Binding in Apex

apexString industry = 'Technology';
Decimal minRevenue = 1000000;

// ✅ Variable binding — safe, compiled, faster
List<Account> accounts = [
    SELECT Id, Name 
    FROM Account 
    WHERE Industry = :industry 
    AND AnnualRevenue > :minRevenue
];

Variable binding:

  • Prevents SOQL injection
  • Improves performance (query is cached/optimized)
  • Reduces errors from string concatenation

5. Use LIMIT in Every Query

Always add a LIMIT unless you have a specific reason to retrieve all records. Even in production data, assume the worst.

6. Understand Relationship Query Directions

  • Child-to-Parent: Dot notation (Account.Name) — always works
  • Parent-to-Child: Subquery with relationship name — only works when a relationship exists

Section 7: Common Mistakes Beginners Make

SOQL

Mistake 1: SOQL Inside Loops

apex// ❌ WRONG — One query per account (hits 101 SOQL limit fast)
for (Account acc : accounts) {
    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}

// ✅ CORRECT — One query for all accounts
Set<Id> accountIds = new Map<Id, Account>(accounts).keySet();
List<Contact> allContacts = [
    SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds
];

Mistake 2: Using String Concatenation with User Input

apex// ❌ SOQL injection vulnerability
String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';

// ✅ Safe approach
String safe = String.escapeSingleQuotes(userInput);
String query = 'SELECT Id FROM Account WHERE Name = \'' + safe + '\'';
// Better: use variable binding wherever possible

Mistake 3: Incorrect Relationship Names

apex// ❌ WRONG — 'Opportunity' is the object name, not the relationship name
SELECT Id, (SELECT Id FROM Opportunity) FROM Account

// ✅ CORRECT — 'Opportunities' is the child relationship name
SELECT Id, (SELECT Id FROM Opportunities) FROM Account

Rule: Standard child relationship names are usually the plural of the object name. Check them in Object Manager → Relationships.

Mistake 4: Comparing Dates Incorrectly

apex// ❌ WRONG — comparing a DateTime field using a Date literal incorrectly
WHERE CreatedDate = '2024-01-15'  // This doesn't work

// ✅ CORRECT — proper date formats
WHERE CreatedDate = TODAY
WHERE CreatedDate >= 2024-01-15T00:00:00Z
WHERE CloseDate = 2024-01-15  // Date field (not DateTime) — this works

Mistake 5: Querying Without Testing in Bulk

Always test your SOQL queries with realistic data volumes. A query returning 10 records in dev sandbox might return 48,000 in production — and behave completely differently.

Mistake 6: Forgetting That SOQL Is Case-Insensitive for Values but Not for API Names

apex// ✅ This works — string values are case-insensitive
WHERE Industry = 'technology'  // Matches 'Technology'

// ❌ This fails — wrong API name (case matters for field names in Apex)
acc.annualrevenue  // Wrong
acc.AnnualRevenue  // Correct

You’ve now covered every major SOQL concept a developer needs — from fundamental syntax to advanced relationship queries, aggregate functions, date literals, semi-joins, dynamic SOQL, and real production patterns.

Key takeaways from this salesforce soql tutorial:

  • ✅ SOQL is the only way to read data from Salesforce programmatically
  • ✅ SOQL is not SQL — it has no JOINs, no SELECT *, and has governor limits
  • ✅ 20 must-know queries cover 90% of real-world development scenarios
  • ✅ Never put SOQL inside loops — collect, query once, process
  • ✅ Selective queries using indexed fields are essential for performance
  • ✅ Dynamic SOQL requires strict input sanitization to prevent injection
  • ✅ Relationship queries replace JOINs with dot notation and subqueries

Where to Practice SOQL

  1. Developer Console — Setup → Developer Console → Query Editor tab
  2. Workbench — workbench.developerforce.com — free, powerful SOQL interface
  3. Salesforce CLI — sf data query --query "SELECT Id FROM Account LIMIT 5"
  4. Trailhead — Complete the “SOQL for Admins” and “Apex Basics & Database” modules
  5. VS Code + Salesforce Extension Pack — Execute SOQL directly from the editor

Your Next Actions

  1. Open your Developer Org right now and run every query in this guide
  2. Modify each query — change filters, add fields, combine techniques
  3. Build an Apex class that uses at least 5 of these query patterns together
  4. Practice the interview questions until you can answer them without notes
  5. Build a real project — create a contact search component or a reporting dashboard using SOQL queries from this guide

SOQL mastery is not about memorizing syntax — it’s about knowing which query pattern solves which problem. The 20 examples in this guide are your toolkit. Practice them until reaching for the right query feels instinctive.

The Developer Console is open. Start querying.

About RizeX Labs

At RizeX Labs, we specialize in delivering cutting-edge Salesforce solutions, including advanced development practices and data-driven architectures within the Salesforce ecosystem. Our expertise combines deep technical knowledge, industry best practices, and real-world implementation experience to help developers and organizations build scalable and efficient Salesforce applications.

We empower professionals to master Salesforce development—from understanding core concepts like SOQL (Salesforce Object Query Language) to implementing optimized queries that improve performance, data retrieval, and application efficiency.


Internal Linking Opportunities:


External Linking Opportunities:


Quick Summary

Salesforce Object Query Language (SOQL) is a powerful tool that enables developers to retrieve and manipulate data efficiently within Salesforce. Mastering SOQL is essential for building scalable applications, optimizing performance, and working effectively with large datasets.

In this tutorial, developers will learn 20 essential SOQL queries that cover everything from basic data retrieval to advanced filtering, relationships, and aggregation. By understanding these queries, developers can reduce execution time, improve data accuracy, and build more efficient Salesforce solutions.

Quick Summary

This comprehensive Salesforce SOQL tutorial provides a complete, practical guide to Salesforce Object Query Language (SOQL) covering everything from fundamental syntax to advanced query patterns that every Salesforce developer must master in 2026. The guide begins by explaining what SOQL is, where it is used across the Salesforce ecosystem including Apex code, Developer Console, REST and SOAP APIs, Salesforce CLI, Workbench, and Flow Builder, and provides a detailed SOQL vs SQL comparison table highlighting critical differences such as SOQL's lack of JOIN support, mandatory field selection instead of SELECT *, governor limits restricting queries to 100 per synchronous transaction and 50,000 records per query, and Salesforce-specific features like rich date literals (TODAY, LAST_N_DAYS, THIS_FISCAL_YEAR) and automatic sharing rule enforcement. The core section delivers 20 production-ready SOQL query examples with real-world scenarios and complete code, covering basic record retrieval, WHERE clause filtering with AND/OR conditions, ORDER BY and LIMIT for sorting and pagination, aggregate functions including COUNT, SUM, AVG, MIN, and MAX, GROUP BY with HAVING for grouped analytics, child-to-parent relationship queries using dot notation (up to 5 levels deep), parent-to-child subqueries using relationship names, LIKE operator for pattern matching with wildcards, comprehensive date literal filters, NULL handling with SOQL-specific syntax (= null and != null instead of SQL's IS NULL), IN clause with both static values and Apex variable binding, OFFSET for implementing record pagination up to 2000 records, semi-joins and anti-joins using subqueries for powerful record filtering, custom object and custom field queries with __c and __r naming conventions, FOR UPDATE record locking for concurrent transaction safety, and dynamic SOQL using Database.query() with proper SOQL injection prevention through String.escapeSingleQuotes() and variable binding. The tutorial also covers inline SOQL versus dynamic SOQL usage in Apex with a clear comparison of when to use each approach, six essential best practices including avoiding SELECT * thinking to conserve heap memory, writing selective queries using indexed fields for performance, respecting governor limits through SOQL for loops and batch processing, using variable binding for security and performance, always applying LIMIT clauses, and understanding relationship query directions. Common beginner mistakes are addressed including putting SOQL inside loops which hits the 101 query limit, SOQL injection vulnerabilities from unsafe string concatenation, using incorrect relationship names in subqueries, improper date and DateTime comparisons, and failing to test queries against production-scale data volumes. The guide concludes with seven frequently asked Salesforce SOQL interview questions with detailed answers covering SOQL vs SOSL differences, maximum record return limits, semi-join and anti-join syntax, COUNT() vs COUNT(fieldName) behavior, governor limit avoidance strategies, child-to-parent vs parent-to-child relationship query patterns, and dynamic SOQL security considerations, making this tutorial an essential resource for Salesforce developers at beginner and intermediate levels preparing for real project work, Salesforce Platform Developer I certification, and technical interviews.

What services does RizeX Labs (formerly Gradx Academy) provide?

RizeX Labs (formerly Gradx Academy) provides practical services solutions designed around customer needs. Our team focuses on clear communication, reliable support, and outcomes that help people make informed decisions quickly.

How can customers get help quickly?

Customers can contact our team directly for fast support, clear next steps, and timely follow-up. We prioritize responsiveness so questions are answered quickly and issues are resolved without unnecessary delays.

Why choose RizeX Labs (formerly Gradx Academy) over alternatives?

Customers choose us for trusted expertise, transparent guidance, and consistent results. We focus on practical recommendations, personalized service, and long-term relationships built on reliability and accountability.

Scroll to Top