Data 360 Integrated Apps
Object Specific APIs
Language-Specific Client Libraries
Data 360 SQL Query APIs
Query Data 360 Data with Apex
SOQL With Apex
Get Started with the Power BI Connector
Get Started with the Python Connector
Use Apex to query Data 360 data directly from your Salesforce org. Access unified customer insights and act on them immediately within your Salesforce automations.
Use the sfsqlquery namespace when you need an easy and intuitive way to execute queries with iterators and typed row accessors.
When to use: Run SQL queries on Data 360
Common workflows:
The namespace provides five classes that support three primary workflows:
Execute a new query synchronously:
SqlStatement → SqlRowIterator → Row
Fetch results of a previously executed query:
QueryHandle → SqlRowIterator → Row
Process large datasets asynchronously:
SqlQueueable → SqlRowIterator → Row
See sfsqlquery Namespace in the Apex Reference.
Apex methods use the current user’s session context and permissions automatically. Ensure users have appropriate Data 360 permissions assigned through permission sets or profiles.
See Getting Started with Apex in the Apex Developer Guide.
LIMIT clauses before processing full datasetsBuild a query with SqlStatement, execute it synchronously with SqlRowIterator, and access typed column values using the Row class. The SqlStatement.create() method builds the query, the execute() method runs it synchronously, and Row provides typed accessor methods like getString() to retrieve column values.
1sfsqlquery.SqlStatement stmt = sfsqlquery.SqlStatement.create(
2 'SELECT ssot__FirstName__c, ssot__LastName__c FROM ssot__Individual__dlm LIMIT 10',
3 'default'
4).withWorkloadName('my_workload');
5
6sfsqlquery.SqlRowIterator iterator = stmt.execute();
7
8for (sfsqlquery.Row row : iterator) {
9 String firstName = row.getString('ssot__FirstName__c');
10 String lastName = row.getString('ssot__LastName__c');
11 System.debug(firstName + ' ' + lastName);
12}Resume a query from a saved query ID using QueryHandle. The QueryHandle.create() method takes a saved query ID and uses withOffset() to skip already-processed results. This is useful when you need to retrieve additional results from a previously executed query.
1String savedQueryId = iterator.getQueryId();
2sfsqlquery.QueryHandle handle = sfsqlquery.QueryHandle.create(savedQueryId, 'default')
3 .withOffset(10);
4sfsqlquery.SqlRowIterator resumedIterator = new sfsqlquery.SqlRowIterator(handle);Process large datasets asynchronously using SqlQueueable. The custom class extends SqlQueueable and implements two key methods: processDataChunk() to handle each page of results, and chainNextJob() to automatically continue processing subsequent pages. This pattern prevents governor limit issues when working with large result sets.
1public class MyQueryJob extends sfsqlquery.SqlQueueable {
2 public MyQueryJob(sfsqlquery.SqlStatement stmt) { super(stmt); }
3 public MyQueryJob(sfsqlquery.QueryHandle handle) { super(handle); }
4
5 public override void processDataChunk() {
6 for (sfsqlquery.Row row : getRows()) {
7 System.debug(row.getString('ssot__FirstName__c'));
8 }
9 }
10
11 public override void chainNextJob(sfsqlquery.QueryHandle handle) {
12 System.enqueueJob(new MyQueryJob(handle));
13 }
14}
15
16sfsqlquery.SqlStatement stmt = sfsqlquery.SqlStatement.create(
17 'SELECT ssot__FirstName__c FROM ssot__Individual__dlm', 'default'
18);
19System.enqueueJob(new MyQueryJob(stmt));For backward compatibility, the ConnectApi.CdpQuery class remains available as a low-level interface that directly mirrors the Connect REST API. We recommend using the sfsqlquery namespace for all new development.
See ConnectApi.CdpQuery Methods in the Apex Reference.