通过 Apex 的 Salesforce 报告和仪表板 API,您可以以编程方式访问您的 报表生成器中定义的报表数据。
该 API 使您能够将报表数据集成到任何 Web 或移动应用程序中,内部或 在 Salesforce 平台之外。例如,您可以使用 API 触发带有 每个季度表现最好的销售代表的快照。
通过 Apex 的 Salesforce 报告和仪表板 API 彻底改变了您访问和 可视化您的数据。您可以:
- 将报表数据集成到自定义对象中。
- 将报表数据集成到丰富的可视化效果中,以动画化 数据。
- 构建自定义仪表板。
- 自动执行报告任务。
概括地说,API 资源使您能够查询和筛选 报告数据。您可以:
- 同步或异步运行表格、摘要或矩阵报表。
- 动态筛选特定数据。
- 查询报表数据和元数据。
- 要求和限制
通过 Apex 的 Salesforce 报告和仪表板 API 适用于启用了 API 的组织。 - 运行报告
您可以通过 Apex 通过 Salesforce 报告和仪表板 API 同步或异步运行报告。 - 列出报表
的异步运行 您最多可以检索异步运行的报表的 2,000 个实例。 - 获取报表元数据
可以检索报表元数据以获取有关报表及其报表类型的信息。 - 获取报表数据 可以使用该类获取事实映射,其中包含与报表关联的数据
。ReportResults - 筛选报告
若要动态获取特定结果,可以通过 API 筛选报告。 - 解码事实数据图
事实数据地图包含报表的摘要和记录级数据值。 - 测试报告
与所有 Apex 代码一样,通过 Apex 代码的 Salesforce 报告和仪表板 API 需要测试覆盖率。
要求和限制
通过 Apex 的 Salesforce 报告和仪表板 API 适用于以下组织 已启用 API。
除了以下限制外,还适用于通过 Apex 的报表和仪表板 API 常规 API 限制。
- 在以下情况下,交叉筛选器、标准报表筛选器和按行限制筛选不可用 筛选数据。
- 只有矩阵报告才支持历史跟踪报告。
- 历史跟踪报告不支持订阅。
- API 只能处理包含最多 100 个选为列的字段的报表。
- 最多可以返回 200 个最近查看的报告的列表。
- 您的组织每小时最多可以请求 500 次同步报表运行。
- API 一次最多支持 20 个同步报表运行请求。
- 异步运行的报表的最多 2,000 个实例的列表可以是 返回。
- 该 API 一次最多支持 200 个请求来获取异步报告的结果 运行。
- 您的组织每小时最多可以请求 1,200 个异步请求。
- 异步报告运行结果在 24 小时滚动周期内可用。
- API 最多返回前 2,000 个报表行。您可以使用以下方法缩小结果范围 过滤 器。
- 运行报表时,您最多可以添加 20 个自定义字段筛选器。
- 如果报表以 Apex 中的自动化流程用户身份在标准或自定义对象上运行 test 类,则仅返回必填的自定义字段。非必填自定义字段不是 结果中显示。
-
- 您的组织每小时最多可以请求 200 次仪表板刷新。
- 您的组织每小时最多可以请求 5,000 个仪表板的结果。
此外,以下限制适用于通过 Apex 的 Reports and Dashboards API。
- 批处理 Apex 中不允许异步报告调用。
- Apex 触发器中不允许报告调用。
- 没有 Apex 方法可以列出最近运行的报告。
- 同步报表运行期间处理的报表行数计入 将 SOQL 查询检索的总行数限制为 50,000 行的调控器限制 每笔交易。异步运行报表时,不会施加此限制。
- 在 Apex 测试中,报告运行始终忽略注释,无论注释是否设置为 或 。这 表示报告结果将包含测试未创建的预先存在的数据。 无法禁用注释 用于执行报表。若要限制结果,请对报表使用筛选器。SeeAllDatatruefalseSeeAllData
- 在 Apex 测试中,异步报告运行仅在测试停止后执行 方法。Test.stopTest
注意
适用于在报表生成器中创建的报表的所有限制也适用于 API。为 有关详细信息,请参阅 Salesforce 联机帮助中的“分析限制”。
运行报表
您可以通过 Salesforce 报表和仪表板同步或异步运行报表 API 通过 Apex。
报表可以在有或没有详细信息的情况下运行,并且可以通过设置报表元数据进行筛选。什么时候 运行报告时,API 将返回相同数量的记录的数据,这些记录在 报告在 Salesforce 用户界面中运行。
如果希望报表快速完成运行,请同步运行报表。否则,我们建议 出于以下原因,您通过 Salesforce API 异步运行报告:
- 长时间运行的报表在达到超时限制时的风险较低 异步运行。
- 通过 Apex 的 Salesforce 报告和仪表板 API 可以处理更多数量的 一次异步运行请求。
- 因为异步运行报表的结果将存储 24 小时 滚动期,它们可用于定期访问。
同步运行报表
运行报表 同步地,使用其中一种方法。例如:ReportManager.runReport()
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Run the report
Reports.ReportResults results = Reports.ReportManager.runReport(reportId, true);
System.debug('Synchronous results: ' + results);
异步运行报表
运行报表 异步使用其中一种方法。例如:ReportManager.runAsyncReport()
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Run the report
Reports.ReportInstance instance = Reports.ReportManager.runAsyncReport(reportId, true);
System.debug('Asynchronous instance: ' + instance);
列出报表的异步运行
您最多可以检索 2,000 个报表实例,这些实例 异步运行。
实例列表按日期和时间排序 报告已运行。报告结果滚动存储 24 小时 时期。在此期间,根据您的用户访问级别,您可以 访问已运行的报表的每个实例的结果。
例
您可以通过调用该方法获取实例列表。 例如:ReportManager.getReportInstances
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Run a report asynchronously
Reports.ReportInstance instance = Reports.ReportManager.runAsyncReport(reportId, true);
System.debug('List of asynchronous runs: ' +
Reports.ReportManager.getReportInstances(reportId));
获取报表元数据
您可以检索报表元数据以获取有关以下内容的信息 报表及其报表类型。元数据包括有关 筛选器、分组、详细数据和摘要的报告。您可以 使用元数据执行以下几项操作:
- 了解您可以在报告中筛选哪些字段和值 类型。
- 使用元数据信息构建自定义图表可视化效果 关于字段、分组、详细数据和摘要。
- 运行报表时更改报表元数据中的筛选器。
使用该方法检索报表元数据。然后,您可以使用“get” 类上的方法来访问元数据值。ReportResults.getReportMetadataReportMetadata
例
下面的示例检索报表的元数据。
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Run a report
Reports.ReportResults results = Reports.ReportManager.runReport(reportId);
// Get the report metadata
Reports.ReportMetadata rm = results.getReportMetadata();
System.debug('Name: ' + rm.getName());
System.debug('ID: ' + rm.getId());
System.debug('Currency code: ' + rm.getCurrencyCode());
System.debug('Developer name: ' + rm.getDeveloperName());
// Get grouping info for first grouping
Reports.GroupingInfo gInfo = rm.getGroupingsDown()[0];
System.debug('Grouping name: ' + gInfo.getName());
System.debug('Grouping sort order: ' + gInfo.getSortOrder());
System.debug('Grouping date granularity: ' + gInfo.getDateGranularity());
// Get aggregates
System.debug('First aggregate: ' + rm.getAggregates()[0]);
System.debug('Second aggregate: ' + rm.getAggregates()[1]);
// Get detail columns
System.debug('Detail columns: ' + rm.getDetailColumns());
// Get report format
System.debug('Report format: ' + rm.getReportFormat());
获取报表数据
可以使用该类获取事实映射,其中包含关联的数据 有一份报告。
ReportResults
例
要访问事实映射的数据值,您可以映射分组 value 键设置为相应的事实映射键。在以下示例中, 想象一下,您有一个已分组的商机报表 按收月,您已经汇总了金额字段。自 获取 报告:
- 使用以下方法获取报表中的第一个缩减分组 并访问第一个对象。ReportResults.getGroupingsDownGroupingValue
- 使用该方法从对象中获取分组键值。GroupingValuegetKey
- 通过追加到此键值来构造事实映射键。生成的事实图键表示 第一个缩减分组的汇总值。‘!T’
- 使用事实地图从报告结果中获取事实地图 钥匙。
- 使用方法获取第一个汇总金额值,并 访问第一个对象。ReportFact.getAggregatesSummaryValue
- 从第一行的第一个数据单元格中获取字段值 的报表。ReportFactWithDetails.getRows
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Run a report synchronously
Reports.reportResults results = Reports.ReportManager.runReport(reportId, true);
// Get the first down-grouping in the report
Reports.Dimension dim = results.getGroupingsDown();
Reports.GroupingValue groupingVal = dim.getGroupings()[0];
System.debug('Key: ' + groupingVal.getKey());
System.debug('Label: ' + groupingVal.getLabel());
System.debug('Value: ' + groupingVal.getValue());
// Construct a fact map key, using the grouping key value
String factMapKey = groupingVal.getKey() + '!T';
// Get the fact map from the report results
Reports.ReportFactWithDetails factDetails =
(Reports.ReportFactWithDetails)results.getFactMap().get(factMapKey);
// Get the first summary amount from the fact map
Reports.SummaryValue sumVal = factDetails.getAggregates()[0];
System.debug('Summary Value: ' + sumVal.getLabel());
// Get the field value from the first data cell of the first row of the report
Reports.ReportDetailRow detailRow = factDetails.getRows()[0];
System.debug(detailRow.getDataCells()[0].getLabel());
筛选报表
要即时获得特定结果,您可以通过 应用程序接口。
通过 API 对筛选器所做的更改不会影响源 报告定义。使用 API,您可以使用最多 20 个自定义字段筛选器进行筛选 并添加筛选器逻辑(例如 AND 和 OR)。但是标准过滤器(如范围), 按行限制进行筛选,并且交叉筛选器不可用。
在筛选报表之前,检查以下筛选器值会很有帮助 在元数据中。
- 该方法告诉您是否可以筛选字段。ReportTypeColumn.getFilterable
- 方法 返回字段的所有筛选器值。ReportTypeColumn.filterValues
- 该方法列出字段 可用于筛选报表的数据类型。ReportManager.dataTypeFilterOperatorMap
- 该方法列出报表中存在的所有筛选器。ReportMetadata.getReportFilters
您可以在同步或异步报表运行期间筛选报表。
例
若要筛选报表,请在报表元数据中设置筛选器值,然后运行报表。 以下示例检索报表元数据,重写筛选器值,然后 运行报表。示例:
- 使用该方法从元数据中检索报表筛选器对象。ReportMetadata.getReportFilters
- 使用方法将筛选器中的值设置为特定日期,然后运行 报告。ReportFilter.setValue
- 将筛选器值覆盖到其他日期并运行报表 再。
该示例的输出显示了不同的总值,基于 已应用的日期筛选器。
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Get the report metadata
Reports.ReportDescribeResult describe = Reports.ReportManager.describeReport(reportId);
Reports.ReportMetadata reportMd = describe.getReportMetadata();
// Override filter and run report
Reports.ReportFilter filter = reportMd.getReportFilters()[0];
filter.setValue('2013-11-01');
Reports.ReportResults results = Reports.ReportManager.runReport(reportId, reportMd);
Reports.ReportFactWithSummaries factSum =
(Reports.ReportFactWithSummaries)results.getFactMap().get('T!T');
System.debug('Value for November: ' + factSum.getAggregates()[0].getLabel());
// Override filter and run report
filter = reportMd.getReportFilters()[0];
filter.setValue('2013-10-01');
results = Reports.ReportManager.runReport(reportId, reportMd);
factSum = (Reports.ReportFactWithSummaries)results.getFactMap().get('T!T');
System.debug('Value for October: ' + factSum.getAggregates()[0].getLabel());
解码事实地图
事实地图包含摘要和记录级别的数据 报表的值。
根据报表的运行方式,报表结果中的事实映射可以包含以下值 仅摘要或摘要和详细数据。事实映射值表示为键,其中 可以通过编程方式用于可视化报表数据。事实图键提供 事实地图的每个部分,您可以从中访问摘要和详细数据。
事实映射键的模式因报表格式而异,如下表所示。
报告格式 | 事实地图键模式 |
---|---|
表格式的 | T!T:报表的总计。两者都记录数据 值和总计由此键表示。 |
总结 | <First level row grouping_second level row grouping_third level row grouping>!T:T表示行大 总。 |
矩阵 | <First level row grouping_second level row grouping>!<First level column grouping_second level column grouping>. |
行或列分组中的每个项目都以 开头编号。以下是事实地图键的一些示例:0
事实地图键 | 描述 |
---|---|
0!T | 第一级分组中的第一项。 |
1!T | 第一级分组中的第二项。 |
0_0!T | 第一级分组中的第一项和第二级分组中的第一项 分组。 |
0_1!T | 第一级分组中的第一项和第二级分组中的第二项 分组。 |
让我们看一下事实映射键如何表示数据的示例,因为它出现在 Salesforce 表格、摘要或矩阵报告。
表格报表事实地图
下面是表格格式的商机报告示例。由于表格报告 没有分组,所有记录级别的数据和摘要都由键表示,键指的是总计。T!T

摘要报告事实地图
此示例显示摘要报告中的值在事实映射中的表示方式。

事实地图键 | 描述 |
0!T | “勘探”阶段机会价值的摘要。 |
1_0!T | 需求分析中制造机会的概率摘要 阶段。 |
矩阵报告事实地图
下面是矩阵商机报告中数据的一些事实映射键的示例,其中 几个行和列分组。

事实地图键 | 描述 |
---|---|
0!0 | 2010 年第 4 季度处于探矿阶段的总机会量。 |
0_0!0_0 | 制造业勘探阶段的总机会量 2010 年 10 月。 |
2_1!1_1 | 技术领域价值主张阶段的机会总价值 在2011年2月。 |
T!T | 报告的总摘要。 |
测试报告
与所有 Apex 代码一样,通过 Apex 代码的 Salesforce 报告和仪表板 API 需要测试 覆盖。
Reporting Apex 方法不在系统模式下运行,而是在 当前用户(也称为上下文用户或登录用户)。这 方法有权访问当前用户有权访问的任何内容。
在 Apex 测试中,无论报告运行如何,报告运行始终忽略注释 批注是否设置为 或 。这 意味着报告结果将包括预先存在的数据,这些数据 测试未创建。无法禁用报表的批注 执行。若要限制结果,请对报表使用筛选器。SeeAllDatatruefalseSeeAllData
创建报表测试类
以下 示例测试异步和同步报表。每种方法:
- 创建一个新的 Opportunity 对象,并使用它来设置筛选器 报告。
- 运行报表。
- 调用断言来验证数据。
注意
在 Apex 测试中,异步报告仅在 使用该方法停止测试。Test.stopTest
@isTest
public class ReportsInApexTest{
@isTest(SeeAllData='true')
public static void testAsyncReportWithTestData() {
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
// Create an Opportunity object.
Opportunity opp = new Opportunity(Name='ApexTestOpp', StageName='stage',
Probability = 95, CloseDate=system.today());
insert opp;
Reports.ReportMetadata reportMetadata =
Reports.ReportManager.describeReport(reportId).getReportMetadata();
// Add a filter.
List<Reports.ReportFilter> filters = new List<Reports.ReportFilter>();
Reports.ReportFilter newFilter = new Reports.ReportFilter();
newFilter.setColumn('OPPORTUNITY_NAME');
newFilter.setOperator('equals');
newFilter.setValue('ApexTestOpp');
filters.add(newFilter);
reportMetadata.setReportFilters(filters);
Test.startTest();
Reports.ReportInstance instanceObj =
Reports.ReportManager.runAsyncReport(reportId,reportMetadata,false);
String instanceId = instanceObj.getId();
// Report instance is not available yet.
Test.stopTest();
// After the stopTest method, the report has finished executing
// and the instance is available.
instanceObj = Reports.ReportManager.getReportInstance(instanceId);
System.assertEquals(instanceObj.getStatus(),'Success');
Reports.ReportResults result = instanceObj.getReportResults();
Reports.ReportFact grandTotal = (Reports.ReportFact)result.getFactMap().get('T!T');
System.assertEquals(1,(Decimal)grandTotal.getAggregates().get(1).getValue());
}
@isTest(SeeAllData='true')
public static void testSyncReportWithTestData() {
// Create an Opportunity Object.
Opportunity opp = new Opportunity(Name='ApexTestOpp', StageName='stage',
Probability = 95, CloseDate=system.today());
insert opp;
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');
Reports.ReportMetadata reportMetadata =
Reports.ReportManager.describeReport(reportId).getReportMetadata();
// Add a filter.
List<Reports.ReportFilter> filters = new List<Reports.ReportFilter>();
Reports.ReportFilter newFilter = new Reports.ReportFilter();
newFilter.setColumn('OPPORTUNITY_NAME');
newFilter.setOperator('equals');
newFilter.setValue('ApexTestOpp');
filters.add(newFilter);
reportMetadata.setReportFilters(filters);
Reports.ReportResults result =
Reports.ReportManager.runReport(reportId,reportMetadata,false);
Reports.ReportFact grandTotal = (Reports.ReportFact)result.getFactMap().get('T!T');
System.assertEquals(1,(Decimal)grandTotal.getAggregates().get(1).getValue());
}
}