afv-library/skills/platform-soql-query/references/cli-commands.md

7.2 KiB

Salesforce CLI SOQL Commands

Quick Reference

Task Command
Run query sf data query --query "SELECT..."
JSON output sf data query --query "..." --json
CSV output sf data query --query "..." --result-format csv
Bulk export sf data export bulk --query "SELECT..." --target-org alias
Query plan sf api request rest '/query/?explain=<SOQL>' --target-org alias

Basic Queries

Run a Query

sf data query \
  --query "SELECT Id, Name, Industry FROM Account LIMIT 10" \
  --target-org my-sandbox

Query with Filters

sf data query \
  --query "SELECT Id, Name FROM Account WHERE Industry = 'Technology'" \
  --target-org my-sandbox

Query Relationships

# Child-to-parent
sf data query \
  --query "SELECT Id, Name, Account.Name FROM Contact LIMIT 10" \
  --target-org my-sandbox

# Parent-to-child
sf data query \
  --query "SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 5" \
  --target-org my-sandbox

Output Formats

Human-Readable (Default)

sf data query \
  --query "SELECT Id, Name FROM Account LIMIT 5" \
  --target-org my-sandbox

JSON

sf data query \
  --query "SELECT Id, Name FROM Account LIMIT 5" \
  --target-org my-sandbox \
  --json

CSV

sf data query \
  --query "SELECT Id, Name, Industry FROM Account" \
  --target-org my-sandbox \
  --result-format csv > accounts.csv

Direct to File

sf data query \
  --query "SELECT Id, Name, Industry FROM Account" \
  --target-org my-sandbox \
  --result-format csv \
  --output-file accounts.csv

Bulk Data Export

For large result sets (> 2,000 records), use the dedicated bulk export command:

# Export to CSV (default)
sf data export bulk \
  --query "SELECT Id, Name FROM Account" \
  --target-org my-sandbox \
  --output-file accounts.csv

# Export as JSON
sf data export bulk \
  --query "SELECT Id, Name FROM Account" \
  --target-org my-sandbox \
  --output-file accounts.json \
  --result-format json

Note

: --bulk and --wait flags on sf data query were removed in v2.87.7. Use sf data export bulk instead.


Query Plan Analysis

Analyze query performance before running:

sf data query \
  --query "SELECT Id FROM Account WHERE Name = 'Acme'" \
  --target-org my-sandbox \
  --use-tooling-api \
  --plan

Understanding Query Plan Output

{
  "plans": [{
    "cardinality": 50,           // Estimated rows returned
    "fields": ["Name"],          // Fields used for filtering
    "leadingOperationType": "Index",  // Index = good, TableScan = bad
    "relativeCost": 0.1,         // Lower is better
    "sobjectCardinality": 10000, // Total records in object
    "sobjectType": "Account"
  }]
}

Key Indicators:

  • leadingOperationType: "Index" = Query uses index (good)
  • leadingOperationType: "TableScan" = Full table scan (bad for large tables)
  • relativeCost < 1 = Efficient query
  • cardinality = Expected number of results

Tooling API Queries

Query metadata objects:

# Query ApexClass
sf data query \
  --query "SELECT Id, Name, Body FROM ApexClass WHERE Name = 'MyController'" \
  --target-org my-sandbox \
  --use-tooling-api

# Query CustomField
sf data query \
  --query "SELECT Id, DeveloperName, TableEnumOrId FROM CustomField WHERE TableEnumOrId = 'Account'" \
  --target-org my-sandbox \
  --use-tooling-api

Query from File

Store query in file and execute:

# Create query file
echo "SELECT Id, Name FROM Account WHERE Industry = 'Technology'" > query.soql

# Execute from file
sf data query \
  --file query.soql \
  --target-org my-sandbox

Useful Patterns

Get Record Count

sf data query \
  --query "SELECT COUNT() FROM Account" \
  --target-org my-sandbox

Export to File

# CSV export
sf data query \
  --query "SELECT Id, Name, Industry, Phone FROM Account" \
  --target-org my-sandbox \
  --result-format csv > accounts.csv

# JSON export
sf data query \
  --query "SELECT Id, Name, Industry FROM Account" \
  --target-org my-sandbox \
  --json > accounts.json

Query with jq Processing

# Get just the names
sf data query \
  --query "SELECT Name FROM Account LIMIT 10" \
  --target-org my-sandbox \
  --json | jq -r '.result.records[].Name'

# Count records
sf data query \
  --query "SELECT Id FROM Account" \
  --target-org my-sandbox \
  --json | jq '.result.totalSize'

# Filter in shell
sf data query \
  --query "SELECT Id, Name, Industry FROM Account" \
  --target-org my-sandbox \
  --json | jq '.result.records[] | select(.Industry == "Technology")'

Query with Dates

# Records created today
sf data query \
  --query "SELECT Id, Name FROM Account WHERE CreatedDate = TODAY" \
  --target-org my-sandbox

# Records from last 30 days
sf data query \
  --query "SELECT Id, Name FROM Account WHERE CreatedDate = LAST_N_DAYS:30" \
  --target-org my-sandbox

Aggregate Queries

# Count by industry
sf data query \
  --query "SELECT Industry, COUNT(Id) FROM Account GROUP BY Industry" \
  --target-org my-sandbox

# Sum of amounts
sf data query \
  --query "SELECT SUM(Amount) FROM Opportunity WHERE StageName = 'Closed Won'" \
  --target-org my-sandbox

Troubleshooting

Query Timeout

For long-running queries, export via bulk API:

sf data export bulk \
  --query "SELECT Id, Name FROM Account" \
  --target-org my-sandbox \
  --output-file results.csv

Too Many Results

Add LIMIT or filter:

# With limit
sf data query \
  --query "SELECT Id, Name FROM Account LIMIT 1000" \
  --target-org my-sandbox

# With filter
sf data query \
  --query "SELECT Id, Name FROM Account WHERE CreatedDate = THIS_YEAR" \
  --target-org my-sandbox

Non-Selective Query Error

Add indexed field to WHERE:

# Add CreatedDate filter (indexed)
sf data query \
  --query "SELECT Id FROM Lead WHERE Status = 'Open' AND CreatedDate = LAST_N_DAYS:90" \
  --target-org my-sandbox

Permission Errors

Check field-level security:

# Query accessible fields only
sf data query \
  --query "SELECT Id, Name FROM Account" \
  --target-org my-sandbox

Integration with Other Tools

Pipe to File

sf data query \
  --query "SELECT Id, Name FROM Account" \
  --target-org my-sandbox \
  --result-format csv | tee accounts.csv

Use in Scripts

#!/bin/bash

ORG=${1:-"my-sandbox"}

# Get count
COUNT=$(sf data query \
  --query "SELECT COUNT() FROM Account" \
  --target-org $ORG \
  --json | jq -r '.result.totalSize')

echo "Total accounts: $COUNT"

# Get top accounts
sf data query \
  --query "SELECT Name, AnnualRevenue FROM Account ORDER BY AnnualRevenue DESC LIMIT 10" \
  --target-org $ORG

Compare Orgs

#!/bin/bash

PROD_COUNT=$(sf data query --query "SELECT COUNT() FROM Account" --target-org prod --json | jq '.result.totalSize')
SANDBOX_COUNT=$(sf data query --query "SELECT COUNT() FROM Account" --target-org sandbox --json | jq '.result.totalSize')

echo "Production accounts: $PROD_COUNT"
echo "Sandbox accounts: $SANDBOX_COUNT"
echo "Difference: $((PROD_COUNT - SANDBOX_COUNT))"