soql-fundamentals

Use this skill when writing or debugging SOQL queries: SELECT syntax, WHERE filters, ORDER BY, LIMIT, OFFSET, relationship queries (child-to-parent and parent-to-child), aggregate functions (COUNT, SUM, AVG, MIN, MAX), and date literals. Trigger keywords: soql, query, SELECT FROM WHERE. NOT for SOQL security enforcement (use soql-security), query optimization and index tuning (use soql-query-optimization), or SOSL full-text search.

Best use case

soql-fundamentals is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use this skill when writing or debugging SOQL queries: SELECT syntax, WHERE filters, ORDER BY, LIMIT, OFFSET, relationship queries (child-to-parent and parent-to-child), aggregate functions (COUNT, SUM, AVG, MIN, MAX), and date literals. Trigger keywords: soql, query, SELECT FROM WHERE. NOT for SOQL security enforcement (use soql-security), query optimization and index tuning (use soql-query-optimization), or SOSL full-text search.

Teams using soql-fundamentals should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/soql-fundamentals/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/apex/soql-fundamentals/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/soql-fundamentals/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How soql-fundamentals Compares

Feature / Agentsoql-fundamentalsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use this skill when writing or debugging SOQL queries: SELECT syntax, WHERE filters, ORDER BY, LIMIT, OFFSET, relationship queries (child-to-parent and parent-to-child), aggregate functions (COUNT, SUM, AVG, MIN, MAX), and date literals. Trigger keywords: soql, query, SELECT FROM WHERE. NOT for SOQL security enforcement (use soql-security), query optimization and index tuning (use soql-query-optimization), or SOSL full-text search.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# SOQL Fundamentals

This skill activates when a practitioner needs to write, debug, or understand Salesforce Object Query Language (SOQL) queries — covering SELECT syntax, filtering, sorting, pagination, relationship traversal, and aggregate functions. It does NOT cover SOQL injection prevention (see soql-security) or query plan optimization (see soql-query-optimization).

---

## Before Starting

Gather this context before working on anything in this domain:

- Confirm whether the query runs in Apex (requires `[ ]` bracket syntax) or an external API context (REST, SOAP, CLI).
- Identify whether the query needs to traverse a relationship (child-to-parent via dot notation or parent-to-child via subquery).
- Check the governor limit context: synchronous Apex allows 100 SOQL queries per transaction; each query can return up to 50,000 rows.
- SOQL does not support arbitrary SQL JOINs, wildcards in field lists (`SELECT *`), or calculation expressions — operations that are standard in SQL but unavailable in SOQL.

---

## Core Concepts

### 1. SELECT … FROM … WHERE Syntax

SOQL queries follow this structure (clauses in square brackets are optional):

```
SELECT fieldList [subquery] [...]
FROM objectType [,...]
[USING SCOPE filterScope]
[WHERE conditionExpression]
[WITH [DATA CATEGORY] filteringExpression]
[GROUP BY { fieldGroupByList | ROLLUP(...) | CUBE(...) }
  [HAVING havingConditionExpression]]
[ORDER BY fieldOrderByList {ASC|DESC} [NULLS {FIRST|LAST}]]
[LIMIT numberOfRowsToReturn]
[OFFSET numberOfRowsToSkip]
[FOR VIEW | FOR REFERENCE]
[FOR UPDATE]
```

In Apex, inline SOQL is wrapped in square brackets:
```apex
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Industry = 'Media' LIMIT 100];
```

Dynamic SOQL uses `Database.query()`:
```apex
String query = 'SELECT Id, Name FROM Account WHERE Industry = :ind LIMIT 100';
List<Account> accounts = Database.query(query);
```

Key constraint: SOQL statements cannot exceed 100,000 characters in length. The 100,000-character limit does not apply to dynamic SOQL in Apex.

### 2. WHERE Clause — Filters and Date Literals

The WHERE clause supports these comparison operators: `=`, `!=`, `<`, `<=`, `>`, `>=`, `LIKE`, `IN`, `NOT IN`, `INCLUDES`, `EXCLUDES`.

**String filtering with LIKE:** `%` matches zero or more characters; `_` matches exactly one character. Comparisons on standard fields are case-insensitive.

**Date literals** allow relative date filtering without hardcoded dates:

| Date Literal | Meaning |
|---|---|
| `TODAY` | Current day (midnight to midnight) |
| `YESTERDAY` | Previous day |
| `THIS_WEEK` | Current week (locale-dependent start day) |
| `LAST_MONTH` | Calendar month before current month |
| `LAST_N_DAYS:n` | Past n days including today |
| `NEXT_N_DAYS:n` | Next n days, not including today |
| `THIS_FISCAL_QUARTER` | Current fiscal quarter |
| `LAST_FISCAL_YEAR` | Previous fiscal year |

Example — open opportunities closing this quarter:
```sql
SELECT Id, Name, CloseDate FROM Opportunity
WHERE CloseDate = THIS_FISCAL_QUARTER AND IsClosed = false
```

**IN operator** for multi-value filters:
```sql
SELECT Name FROM Account WHERE BillingState IN ('California', 'New York')
```

Strings in WHERE clauses cannot exceed 4,000 characters (this limit does not apply to Apex `IN` clauses).

### 3. Relationship Queries

SOQL supports two types of relationship traversal:

**Child-to-parent (dot notation):** Traverse up to five levels. Use the relationship name with dot notation:
```sql
SELECT Id, Name, Account.Name, Account.BillingCity FROM Contact WHERE Account.Industry = 'Media'
```
For custom relationship fields, append `__r` instead of `__c`:
```sql
SELECT Id, Mother_of_Child__r.FirstName__c FROM Daughter__c
```

**Parent-to-child (subquery):** Query related child records in a nested SELECT. Use the child relationship name (plural for standard, `__r` for custom):
```sql
SELECT Name, (SELECT LastName, Email FROM Contacts) FROM Account WHERE Industry = 'Media'
```
Custom child relationship:
```sql
SELECT Name, (SELECT Name FROM Line_Items__r) FROM Merchandise__c WHERE Name LIKE 'Acme%'
```

Relationship query limits (from SOQL and SOSL Reference):
- Maximum relationship levels in a query: 5 (child-to-parent traversal)
- Maximum parent-to-child subqueries per query: 20
- A subquery cannot itself contain a subquery

### 4. Aggregate Functions and GROUP BY

SOQL aggregate functions: `COUNT()`, `COUNT(fieldName)`, `COUNT_DISTINCT()`, `AVG()`, `MIN()`, `MAX()`, `SUM()`.

All aggregate functions ignore null values, except `COUNT()` and `COUNT(Id)`.

```sql
-- Count records matching criteria
SELECT COUNT() FROM Account WHERE Industry = 'Media'

-- Aggregate by group
SELECT LeadSource, COUNT(Name) FROM Lead GROUP BY LeadSource

-- Filter groups with HAVING (like a WHERE for aggregates)
SELECT LeadSource, COUNT(Name) FROM Lead
GROUP BY LeadSource HAVING COUNT(Name) > 100

-- Find duplicates
SELECT Name, COUNT(Id) FROM Account GROUP BY Name HAVING COUNT(Id) > 1
```

Key rule: you cannot use `LIMIT` in a query that uses an aggregate function without also using `GROUP BY`. `MAX(CreatedDate) FROM Account LIMIT 1` is **invalid**. Adding `GROUP BY SomeField LIMIT 5` is valid.

### 5. ORDER BY, LIMIT, and OFFSET

**ORDER BY** — Results are not guaranteed to be in any consistent order unless you include ORDER BY. For stable pagination, always include a tiebreaker (e.g., `Id`):
```sql
SELECT Name, Industry FROM Account ORDER BY Industry ASC, Id NULLS LAST
```
`NULLS FIRST` (default) and `NULLS LAST` control where null values appear.

**LIMIT** — Returns at most n rows. Maximum 50,000 records per query in most contexts.
```sql
SELECT Name FROM Account WHERE Industry = 'Media' LIMIT 125
```

**OFFSET** — Skips the first n rows for server-side pagination. Maximum offset is 2,000 rows. Always pair with `ORDER BY` for consistent results:
```sql
SELECT Name, Id FROM Merchandise__c ORDER BY Name LIMIT 100 OFFSET 100
```
OFFSET is NOT allowed in Bulk API or Streaming API queries.

---

## Common Patterns

### Pattern 1 — Semi-Join (IN Subquery)

**When to use:** Filter parent records based on a condition on child records, without loading all child data.

```sql
-- Accounts that have at least one closed-lost opportunity
SELECT Id, Name FROM Account
WHERE Id IN (
  SELECT AccountId FROM Opportunity WHERE StageName = 'Closed Lost'
)
```

**Constraint:** The subquery must return a single foreign key or ID field. The selected column cannot use dot notation (no relationship traversal inside a subquery field list).

### Pattern 2 — Anti-Join (NOT IN Subquery)

**When to use:** Find records that do NOT have matching child/related records.

```sql
-- Accounts with no open opportunities
SELECT Id, Name FROM Account
WHERE Id NOT IN (
  SELECT AccountId FROM Opportunity WHERE IsClosed = false
)
```

### Pattern 3 — FIELDS() Keyword for Schema Exploration

**When to use:** Discover all fields on an object without knowing field names. Available since API v51.0.

```sql
SELECT FIELDS(ALL) FROM Account LIMIT 200
SELECT FIELDS(STANDARD) FROM Contact
SELECT FIELDS(CUSTOM) FROM Account LIMIT 200
```

`FIELDS(ALL)` respects FLS — it only returns fields the running user can read. Always pair with `LIMIT` when using `FIELDS(ALL)` or `FIELDS(CUSTOM)` to avoid hitting governor limits.

---

## Decision Guidance

| Situation | Recommended Approach | Reason |
|---|---|---|
| Need records from one object with fields from parent | Child-to-parent dot notation in SELECT | Flat result set, up to 5 levels |
| Need parent record plus all related child records | Parent-to-child subquery | Returns nested result set |
| Need to count or aggregate for reporting | GROUP BY + aggregate function | Server-side aggregation; no Apex loop needed |
| Need paginated results for UI | LIMIT + OFFSET with ORDER BY tiebreaker | Stable pagination; max offset 2,000 |
| Need records from last N days dynamically | Date literal (LAST_N_DAYS:n) | Avoids hardcoded dates that break across orgs |
| Need records only the current user owns | USING SCOPE mine | Returns only records owned by running user |
| Need cross-object filtering without loading all records | Semi-join (IN subquery) | Efficient server-side filter |

---


## Recommended Workflow

Step-by-step instructions for an AI agent or practitioner activating this skill:

1. Gather context — confirm the org edition, relevant objects, and current configuration state
2. Review official sources — check the references in this skill's well-architected.md before making changes
3. Implement or advise — apply the patterns from Core Concepts and Common Patterns sections above
4. Validate — run the skill's checker script and verify against the Review Checklist below
5. Document — record any deviations from standard patterns and update the template if needed

---

## Review Checklist

Run through these before marking SOQL work complete:

- [ ] Every field in SELECT has at least read-level FLS for the running user (consider WITH USER_MODE in Apex)
- [ ] WHERE clause fields are indexed or selective to avoid full table scans
- [ ] Relationship traversal does not exceed 5 levels (child-to-parent) or 20 subqueries (parent-to-child)
- [ ] LIMIT is present on queries that could return large result sets inside Apex triggers
- [ ] ORDER BY tiebreaker (e.g., Id) added when using OFFSET for pagination
- [ ] Date literals used instead of hardcoded date strings for relative date filtering
- [ ] SOQL injection risk reviewed if any part of the query is dynamically constructed (see soql-security skill)

---

## Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

1. **Results are unordered without ORDER BY** — The Salesforce query engine does not guarantee a consistent row order unless ORDER BY is specified. Code that relies on "first record returned" without ORDER BY can return different records in different transactions or releases. Always add ORDER BY + Id as a tiebreaker.

2. **OFFSET max is 2,000 rows** — Requesting OFFSET beyond 2,000 raises `NUMBER_OUTSIDE_VALID_RANGE`. For result sets larger than 2,000, use `queryMore()` (SOAP) or `nextRecordsUrl` (REST) instead of incrementing OFFSET. OFFSET is not a replacement for cursor-based pagination on large data sets.

3. **Aggregate functions cannot use LIMIT without GROUP BY** — `SELECT MAX(CreatedDate) FROM Account LIMIT 1` is invalid. The pattern that looks like "get the most recent record" does not work this way in SOQL — use `ORDER BY CreatedDate DESC LIMIT 1` on a full query instead.

4. **FIELDS(ALL) without LIMIT causes row-count failures** — `SELECT FIELDS(ALL) FROM Account` returns all fields for every record. Without LIMIT, this easily blows the 50,000-row limit or produces a QUERY_TOO_LARGE error. Always pair FIELDS() with LIMIT.

5. **Custom relationship names use __r not __c** — Traversing a custom lookup field `Parent__c` in a query uses `Parent__r.FieldName__c`, not `Parent__c.FieldName__c`. Using `__c` in dot notation causes a MALFORMED_QUERY error that is easy to misread.

---

## Output Artifacts

| Artifact | Description |
|---|---|
| Validated SOQL statement | A syntactically correct SOQL query ready to use in Apex, REST API, or Developer Console |
| Relationship path diagram | Textual description of how objects connect for relationship query traversal |
| Aggregate query template | GROUP BY + HAVING pattern for the specific reporting need |

---

## Related Skills

- soql-security — Use alongside this skill to add WITH USER_MODE or WITH SECURITY_ENFORCED to enforce FLS and CRUD in Apex queries
- soql-query-optimization — Use when the query is correct but slow or hitting CPU limits due to non-selective filters or missing indexes
- apex-cpu-and-heap-optimization — Use when SOQL governor limits (100 queries, 50,000 rows) are being hit in a transaction

Related Skills

soql-query-optimization

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when a SOQL query is running slowly, causing timeouts, or returning UNABLE_TO_LOCK_ROW errors in large data volume orgs. Covers index-aware query writing, selectivity rules, the Query Plan tool, skinny tables, and dynamic field-set queries. Triggers: slow soql query, query timeout, non-selective query, query plan tool, index usage, soql optimization, large object performance. NOT for Apex CPU or heap governor limit issues (use apex-cpu-and-heap-optimization) or for writing basic SOQL (use soql-fundamentals).

visualforce-fundamentals

8
from PranavNagrecha/AwesomeSalesforceSkills

Designing and debugging Visualforce pages: standard/custom controllers, view state management, CSRF and SOQL injection security, PDF rendering, Visualforce email templates. Use when building custom UI pages or PDF outputs on the Salesforce platform. NOT for LWC development (use lwc/* skills). NOT for Visualforce email template syntax (use email-services).

soql-security

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when writing, reviewing, or troubleshooting Apex queries that may expose SOQL injection or CRUD/FLS issues. Triggers: 'Database.query', 'WITH USER_MODE', 'WITH SECURITY_ENFORCED', 'stripInaccessible', 'security review finding'. NOT for record-sharing design unless the main issue is Apex query security.

soql-null-ordering-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when SOQL ORDER BY behavior with NULL values surprises a query — null records sorting before non-null, paginated results inconsistent across pages, NULLS FIRST/LAST clauses needed. Triggers: 'soql nulls first', 'soql null sort order', 'pagination missing records with null fields', 'order by skipping null records', 'consistent ordering with optional fields'. NOT for general SOQL optimization (use data/soql-query-optimization) or for ordering of relationship-traversed fields.

apex-soql-relationship-queries

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when writing or debugging SOQL relationship queries in Apex — child-to-parent dot notation traversal, parent-to-child subqueries, and polymorphic TYPEOF lookups. Trigger keywords: relationship query, subquery, dot notation, getSObjects, TYPEOF, WhatId, WhoId. NOT for aggregate queries (use apex-aggregate-queries), NOT for SOSL text search, NOT for Bulk API data loads (subqueries unsupported there).

apex-polymorphic-soql

8
from PranavNagrecha/AwesomeSalesforceSkills

Polymorphic SOQL with TYPEOF: querying Task.WhatId, Task.WhoId, ContentDocumentLink.LinkedEntityId, FeedItem.ParentId; fallback to Type filters; indexing and selectivity. NOT for Activity object model (use activity-and-task-patterns). NOT for general SOQL (use apex-soql-patterns).

apex-dynamic-soql-binding-safety

8
from PranavNagrecha/AwesomeSalesforceSkills

Safe construction of dynamic SOQL — Database.query bind variables (:varName, API 60+ semantics), Database.queryWithBinds(query, Map<String,Object>, AccessLevel) (API 55+), field-name allowlisting, ORDER BY direction whitelist, LIMIT/OFFSET typing, and the interaction with WITH USER_MODE / WITH SECURITY_ENFORCED. NOT for static SOQL — see apex-soql-fundamentals. NOT for FLS enforcement on results — see soql-security or apex-stripinaccessible-and-fls-enforcement.

reports-and-dashboards-fundamentals

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when learning, designing, or explaining Salesforce Reports and Dashboards from first principles — report types, custom report types, groupings, bucket fields, summary formulas, charts, dashboard components, dynamic dashboards, report subscriptions, and folder permissions. Triggers: 'how do I build a report', 'what report type should I use', 'set up a dashboard', 'joined report limits', 'bucket field vs formula', 'dynamic dashboard running user'. NOT for CRM Analytics (use crm-analytics-* skills). NOT for Einstein Discovery predictive analytics. NOT for troubleshooting missing report data caused by sharing model issues (use admin/reports-and-dashboards for that).

xss-and-injection-prevention

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when writing or reviewing Visualforce pages, Apex controllers, or LWC components that output user-supplied data, build dynamic queries, or construct HTTP responses. Triggers: 'XSS in Visualforce', 'SOQL injection vulnerability', 'how to encode output in Apex', 'JSENCODE Visualforce', 'open redirect prevention'. NOT for Apex CRUD/FLS enforcement (use soql-security or apex-crud-and-fls), NOT for Shield encryption (use shield-encryption-key-management), NOT for AppExchange security review process (use secure-coding-review-checklist).

visualforce-security-and-modernization

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when hardening or modernizing legacy Visualforce pages — covers the platform CSRF token model and when disabling it is a security regression, view state encryption guarantees and the 170 KB ceiling, FLS/CRUD enforcement gaps on `<apex:outputField>` and on getters that return sObjects, `<apex:includeScript>` interaction with the org Content Security Policy, hosting LWC inside a VF page via `lightning:container` / `lightning-out`, and the retire-vs-harden-vs-leave-alone decision for an inventory of legacy pages. Triggers: 'should I rewrite this Visualforce page in LWC', 'CSRF protection disabled on Visualforce page is that safe', 'community user sees a field they should not on a Visualforce page', 'view state encryption is that enough for sensitive data', 'how do I host an LWC inside a Visualforce page', 'apex:dynamicComponent and apex:actionFunction safe to keep'. NOT for greenfield Visualforce architecture (use apex/visualforce-fundamentals — controller types, view state pattern selection, PDF rendering); NOT for Visualforce email template authoring (use apex/visualforce-email-templates if/when that skill is authored); NOT for general Apex security review across triggers and async (use apex/soql-security and security/secure-coding-review-checklist).

transaction-security-policies

8
from PranavNagrecha/AwesomeSalesforceSkills

Transaction Security policy creation and configuration: condition builder, enhanced policies, enforcement actions (block, MFA, notification, end session), real-time monitoring mode, and policy troubleshooting. NOT for Event Monitoring log analysis or Shield Event Monitoring setup (use event-monitoring). NOT for Apex testing or debug-log analysis.

sso-saml-troubleshooting

8
from PranavNagrecha/AwesomeSalesforceSkills

Diagnosing broken SAML SSO into Salesforce — IdP-initiated vs SP-initiated flows, signing-certificate validity / expiry, NameID format mismatches, RelayState handling, audience / entityId / issuer mismatches, clock skew, the SAML Assertion Validator in Setup, the Login History debug log, and the My Domain prerequisite for SSO. Covers the standard diagnostic loop: read the SAML response, identify which check failed, fix at the IdP or SP. NOT for OAuth / OpenID Connect SSO (see security/oauth-openid-troubleshooting), NOT for setting up SSO from scratch (see security/sso-saml-setup).