Sf soql
Salesforce development skills for AI coding agents - Apex, Flows, LWC, SOQL, security, deployments. Works with Claude Code, Cursor, Codex, and 50+ tools.
npx -y skills add Clientell-Ai/salesforce-skills --skill sf-soqlAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 11 stars11 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Build and optimize SOQL queries including relationship queries, aggregate functions, polymorphic TYPEOF, and selective filters. Use when asked to write queries, debug slow queries, optimize existing SOQL, or enforce query security. Activate on mentions of "SOQL", "query", "SELECT", "WHERE", "aggregate", "relationship query", or "query optimization".
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.6 KB, as published. Nobody here has run it
SOQL Query Builder & Optimizer
You are a Salesforce SOQL specialist. Build optimized, secure queries.
Security First
- ALWAYS use
WITH USER_MODEto enforce CRUD/FLS - NEVER use string concatenation for dynamic SOQL — use bind variables
- Use
Database.query()only when dynamic queries are truly needed
// GOOD
List<Account> accounts = [
SELECT Id, Name, Industry
FROM Account
WHERE Industry = :industryFilter
WITH USER_MODE
LIMIT 200
];
// BAD — injection risk
String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';
Query Patterns
Parent-to-Child (Subquery)
SELECT Id, Name,
(SELECT Id, FirstName, LastName FROM Contacts)
FROM Account
WHERE Industry = 'Technology'
WITH USER_MODE
Child-to-Parent (Dot Notation)
SELECT Id, FirstName, Account.Name, Account.Industry
FROM Contact
WHERE Account.Industry = 'Technology'
WITH USER_MODE
Aggregate Queries
SELECT Industry, COUNT(Id) cnt, SUM(AnnualRevenue) totalRevenue
FROM Account
WHERE Industry != null
WITH USER_MODE
GROUP BY Industry
HAVING COUNT(Id) > 5
ORDER BY COUNT(Id) DESC
Polymorphic (TYPEOF)
SELECT Id, Subject,
TYPEOF What
WHEN Account THEN Name, Industry
WHEN Opportunity THEN Name, StageName, Amount
END
FROM Task
WITH USER_MODE
Semi-Joins and Anti-Joins
-- Semi-join: Accounts WITH contacts
SELECT Id, Name FROM Account
WHERE Id IN (SELECT AccountId FROM Contact)
WITH USER_MODE
-- Anti-join: Accounts WITHOUT opportunities
SELECT Id, Name FROM Account
WHERE Id NOT IN (SELECT AccountId FROM Opportunity)
WITH USER_MODE
SOSL (Search Language)
Use SOSL for full-text search across multiple objects:
FIND {SearchTerm} IN ALL FIELDS
RETURNING Account(Id, Name WHERE Industry = 'Tech'),
Contact(Id, FirstName, LastName)
LIMIT 20
- Use SOSL when: searching text across objects, fuzzy matching, partial words
- Use SOQL when: exact matches, relationship queries, aggregates, DML-related queries
- Governor limit: 20 SOSL queries per transaction
Date Literals
| Literal | Meaning |
|---|---|
TODAY, YESTERDAY, TOMORROW | Calendar day |
THIS_WEEK, LAST_WEEK, NEXT_WEEK | Sun-Sat week |
THIS_MONTH, LAST_MONTH, NEXT_MONTH | Calendar month |
THIS_QUARTER, LAST_QUARTER | Calendar quarter |
THIS_YEAR, LAST_YEAR, NEXT_YEAR | Calendar year |
LAST_N_DAYS:n | Past n days (includes today) |
NEXT_N_DAYS:n | Next n days (includes today) |
LAST_90_DAYS | Past 90 days |
THIS_FISCAL_QUARTER, THIS_FISCAL_YEAR | Fiscal periods |
N_DAYS_AGO:n | Exactly n days ago |
FIELDS() Functions
SELECT FIELDS(ALL) FROM Account LIMIT 200 -- All fields (LIMIT required)
SELECT FIELDS(STANDARD) FROM Account -- Standard fields only
SELECT FIELDS(CUSTOM) FROM Account -- Custom fields only
Dynamic SOQL
String query = 'SELECT Id, Name FROM Account WHERE Industry = :industry';
List<Account> results = Database.query(query, AccessLevel.USER_MODE);
- Always use
AccessLevel.USER_MODEwithDatabase.query() - Use
:bindVariablesyntax — never string concatenation - For truly dynamic field names:
String.escapeSingleQuotes()
Utility Functions
toLabel(PicklistField)— returns translated picklist labelFORMAT(NumberField)— locale-formatted number/dateconvertCurrency(Amount)— converts to user's currency (multi-currency orgs)
Record Locking
SELECT Id, Name FROM Account WHERE Id = :accountId FOR UPDATE
Pessimistic lock — blocks other transactions from updating until commit/rollback.
ALL ROWS (Including Deleted)
SELECT Id, Name FROM Account WHERE IsDeleted = true ALL ROWS
Returns soft-deleted records (retained 15 days in Recycle Bin).
SOQL For Loops
for (List<Account> batch : [SELECT Id, Name FROM Account]) {
// Processes 200 records per iteration automatically
// Uses minimal heap — ideal for large datasets
}
Geolocation Queries
SELECT Id, Name, DISTANCE(Location__c, GEOLOCATION(37.7749, -122.4194), 'mi') dist
FROM Store__c
WHERE DISTANCE(Location__c, GEOLOCATION(37.7749, -122.4194), 'mi') < 50
ORDER BY DISTANCE(Location__c, GEOLOCATION(37.7749, -122.4194), 'mi')
WITH SECURITY_ENFORCED vs WITH USER_MODE
| Feature | SECURITY_ENFORCED | USER_MODE |
|---|---|---|
| FLS enforcement | SELECT/FROM only | SELECT/FROM/WHERE/subqueries |
| On violation | Throws exception | Silently strips inaccessible fields |
| Restriction rules | Not supported | Supported |
| Recommendation | Legacy — migrate away | Preferred |
Optimization Rules
Selective Filters (use indexed fields)
Id,Name,OwnerId,CreatedDate,SystemModstampRecordTypeId,Lookupfields,External IDfields- Custom fields marked as
External IDor with custom index
Anti-Patterns to Detect
- Query in loop — move query before the loop, use Map/Set
- Non-selective filter — filter on indexed fields first
- SELECT * equivalent — never select all fields, only what's needed
- Missing LIMIT — add LIMIT for queries that could return large datasets
- Negative filters —
!=andNOT INare non-selective - Leading wildcard —
LIKE '%term'cannot use indexes - Missing WHERE clause — always filter unless deliberately loading all
Query Plan Analysis
sf data query -q "EXPLAIN SELECT Id FROM Account WHERE Name = 'Test'" --target-org myOrg
Limits
- 100 SOQL queries per synchronous transaction
- 200 SOQL queries per asynchronous transaction
- 50,000 rows returned per transaction
- 2,000 rows in a subquery
- 20 relationship queries per parent query
Gotchas
FIELDS(ALL)REQUIRESLIMIT 200— fails without itCOUNT()counts all rows including nulls;COUNT(fieldName)counts non-null onlyFOR UPDATElocks the ENTIRE row — other transactions wait or timeout- SOSL has its own governor limit: 20 queries/transaction (separate from SOQL's 100)
- Date literals include the boundary day —
LAST_N_DAYS:7includes today TYPEOFonly works on polymorphic fields (Task.What, Event.Who, etc.)- Subquery result limit is 2,000 rows — not 50,000
FIELDS(ALL)is not supported in Apex — only REST API and Developer ConsoleDatabase.query()does not support FIELDS() — use explicit field listsALL ROWScannot be used withFOR UPDATE
Workflow
- Understand the data requirements
- Check object relationships and field types
- Build query with proper filters, security, and limits
- Test with:
sf data query -q "YOUR_QUERY" --target-org myOrg - Optimize based on results and explain plan
References
- SOQL/SOSL Reference — date literals, SOSL syntax, FIELDS(), geolocation, dynamic SOQL, aggregates, FOR UPDATE, bind patterns, query plans
- Governor Limits — SOQL query limits per transaction