Salesforce SOQL vs SQL: Key Differences Every Developer Should Know

Author : Cloud shukla2 | Published On : 07 Jul 2026

If you've spent any time with relational databases before stepping into Salesforce development, SOQL will feel familiar enough to be comfortable — and just different enough to trip you up in ways that aren't always obvious at first.

The surface-level resemblance is real. SOQL uses SELECT, FROM, WHERE, ORDER BY, and LIMIT. If you've written SQL queries before, your first SOQL query will probably look reasonable and even run correctly. Then you'll hit something unexpected — a relationship you can't join the way you're used to, a subquery that doesn't work the way SQL subqueries do, a governor limit error that SQL developers have never encountered — and you'll realize the two languages, despite appearances, operate on fundamentally different assumptions.

This post covers those differences systematically. Not just what SOQL can't do that SQL can, but why — and what the SOQL equivalents or workarounds look like in practice. Whether you're an experienced database developer moving into Salesforce or a Salesforce professional trying to sharpen your query skills, this is the comparison guide worth bookmarking.

What Is SOQL? The Foundation Before the Comparison

SOQL stands for Salesforce Object Query Language. It's a query language designed specifically for retrieving records from Salesforce's cloud database — the Force.com platform — and it's the primary way Apex code interacts with Salesforce data.

Like SQL, SOQL lets you retrieve specific fields from specific objects based on filter conditions. Unlike SQL, SOQL operates within the constraints of Salesforce's multi-tenant cloud architecture, which introduces both limitations and features that have no direct equivalent in traditional relational databases.

SOQL is used in:

  • Apex classes and triggers to retrieve records programmatically

  • Developer Console to query Salesforce data directly

  • Salesforce CLI and workbench tools for data inspection

  • Flow and other declarative tools (in the background, even when you don't see the query)

SOQL is read-only. It retrieves records — it does not insert, update, or delete them. That's handled separately via DML (Data Manipulation Language) statements in Apex. This is a fundamental structural difference from SQL, where SELECT, INSERT, UPDATE, and DELETE are all part of the same language.

Side-by-Side: SOQL vs SQL at a Glance

Before diving into specifics, here's a high-level comparison table that maps the conceptual territory:

Feature

SQL

SOQL

Language purpose

Query, insert, update, delete

Query only (read-only)

Data source

Relational database tables

Salesforce objects

JOIN support

Full JOIN support (INNER, LEFT, RIGHT, FULL)

No traditional JOINs — uses relationship traversal

Subqueries

Flexible, nested subqueries

Limited, relationship-based subqueries only

Wildcard field selection

SELECT * supported

SELECT * NOT supported — must list fields explicitly

Aggregate functions

COUNT, SUM, AVG, MIN, MAX

COUNT, COUNT_DISTINCT, SUM, AVG, MIN, MAX

Governor limits

None (database-level limits only)

Strict: 100 queries/transaction, 50,000 records max

Full-text search

LIKE with wildcards

Handled by SOSL (separate language)

Date functions

Extensive built-in date functions

Date literals (THIS_WEEK, LAST_MONTH, etc.)

NULL handling

IS NULL / IS NOT NULL

#ERROR!

Schema

Tables with foreign keys

Objects with relationship fields

 

Difference 1: SELECT * Does Not Exist in SOQL

In SQL, the wildcard select is one of the first things you learn:

sql

-- SQL: Works fine

SELECT * FROM Account;

In SOQL, this does not work. Full stop.

apex

// SOQL: This will throw an error

SELECT * FROM Account  // INVALID

 

// SOQL: You must explicitly list every field you want

SELECT Id, Name, Industry, AnnualRevenue, OwnerId FROM Account

This isn't an oversight — it's a deliberate design choice tied to Salesforce's multi-tenant architecture and governor limits. Every field you retrieve consumes heap memory and counts toward query performance limits. Forcing explicit field declaration keeps queries efficient and prevents accidental over-retrieval of sensitive or unnecessary data.

Practical implication: Before writing SOQL in Apex, you need to know which fields you actually need. Get into the habit of querying only the fields your code will actually use — both for governor limit efficiency and for security reasons (you shouldn't retrieve sensitive field data your code doesn't need).

Difference 2: SOQL Uses Relationship Traversal, Not JOINs

This is the difference that catches SQL developers most off guard. Traditional SQL uses JOIN syntax to combine data from multiple tables:

sql

-- SQL JOIN: Standard approach

SELECT Account.Name, Contact.FirstName, Contact.LastName

FROM Contact

INNER JOIN Account ON Contact.AccountId = Account.Id

WHERE Account.Industry = 'Technology';

SOQL has no JOIN keyword. Instead, it uses relationship traversal — dot-notation to walk between related objects in a single query.

Parent-to-Child Relationship Queries (Subquery approach)

When you want data from a parent object and related child records:

apex

// SOQL: Get Accounts with their related Contacts

SELECT Id, Name,

       (SELECT FirstName, LastName, Email FROM Contacts)

FROM Account

WHERE Industry = 'Technology'

Note: The inner query (SELECT ... FROM Contacts) uses the relationship name — not the object API name. For standard objects, this is often the plural form (Contacts, Opportunities). For custom objects, it's the relationship name defined on the field plus __r.

Child-to-Parent Relationship Queries (Dot-notation)

When you're querying child records and want fields from the parent:

apex

// SOQL: Get Contacts with their parent Account name

SELECT FirstName, LastName, Email, Account.Name, Account.Industry

FROM Contact

WHERE Account.Industry = 'Technology'

Here Account.Name traverses the relationship from Contact to its parent Account using dot-notation. You can traverse up to 5 levels of parent relationships in a single query.

What's Missing Compared to SQL JOINs?

  • No LEFT JOIN equivalent for unrelated objects. If two Salesforce objects have no relationship field connecting them, you cannot query them together in SOQL. SQL can JOIN any two tables that share a common value; SOQL can only traverse defined Salesforce relationships.

  • No FULL OUTER JOIN. SOQL subqueries in parent-to-child direction return null for parents with no children (similar to LEFT JOIN behavior), but there's no equivalent of FULL OUTER JOIN.

  • No cross-object aggregation without relationships. If you need to aggregate data across objects that aren't related in Salesforce, you'd need to make separate SOQL queries and handle the join logic in Apex.

Difference 3: Governor Limits Are a Hard Reality in SOQL

This is perhaps the most operationally significant difference between SOQL and SQL — and it's one that SQL developers almost always underestimate when they first start working in Salesforce.

In a traditional database, query limits are soft — the database might slow down if you query too much, but it rarely throws an exception that crashes your application. In Salesforce, governor limits are hard runtime exceptions that terminate your transaction immediately when breached.

Key SOQL governor limits per synchronous Apex transaction:

Limit

Cap

Total SOQL queries

100

Total records retrieved by SOQL

50,000

Total records retrieved by a single Database.getQueryLocator

10,000 (50 million in Batch Apex)

Total heap size

6 MB

These limits exist because Salesforce is a multi-tenant platform — every org shares the same infrastructure. If one tenant's runaway query consumed unlimited resources, every other org on the same server would suffer. Governor limits are the architectural solution to that problem.

What this means for SOQL in practice:

You can never write SOQL inside a loop. This is the cardinal rule of Salesforce development, and it's the most common mistake SQL developers make when they transition to Apex:

apex

// WRONG — SOQL inside a loop: will hit 101 SOQL limit error

for (Account acc : accountList) {

    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];

    // Do something

}

 

// RIGHT — Single SOQL query with IN clause, process in Apex

Set<Id> accountIds = new Map<Id, Account>(accountList).keySet();

List<Contact> contacts = [SELECT Id, AccountId FROM Contact 

                          WHERE AccountId IN :accountIds];

 

Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();

for (Contact c : contacts) {

    if (!contactsByAccount.containsKey(c.AccountId)) {

        contactsByAccount.put(c.AccountId, new List<Contact>());

    }

    contactsByAccount.get(c.AccountId).add(c);

}

The IN clause with a set of Ids replaces what a SQL developer would do with a JOIN — one query retrieves all related records at once, and the filtering is done in Apex memory.

Difference 4: SOQL Date Handling Is Completely Different

SQL databases typically have rich date function libraries:

sql

-- SQL: Date functions

SELECT * FROM Opportunity 

WHERE YEAR(CloseDate) = 2026 

AND MONTH(CloseDate) = 6;

 

SELECT DATEDIFF(day, CreatedDate, CloseDate) AS DaysToClose 

FROM Opportunity;

SOQL takes a different approach — it uses date literals rather than date functions:

apex

// SOQL date literals

SELECT Id, Name, CloseDate FROM Opportunity 

WHERE CloseDate = THIS_YEAR;

 

SELECT Id, Name, CloseDate FROM Opportunity 

WHERE CloseDate > LAST_N_DAYS:30;

 

SELECT Id, Name, CloseDate FROM Opportunity 

WHERE CloseDate = THIS_MONTH;

Common SOQL date literals:

Date Literal

What It Means

TODAY

Current day

YESTERDAY

Previous day

THIS_WEEK

Current week (Sunday to Saturday)

LAST_WEEK