Salesforce Apex (基础4)SOQL

学习目标

完成本单元后,您将能够:

  • 在Apex中编写SOQL查询。
  • 通过在开发者控制台中使用查询编辑器执行SOQL查询。
  • 通过使用匿名Apex执行嵌入在Apex中的SOQL查询。
  • 查询相关记录。

编写SOQL查询

要从Salesforce中读取记录,您需要编写一个查询。 Salesforce提供Salesforce对象查询语言(简称SOQL),您可以使用它来读取保存的记录。 SOQL类似于标准的SQL语言,但是为Force.com平台定制。
由于Apex可直接访问存储在数据库中的Salesforce记录,因此可以将SOQL查询嵌入到Apex代码中,并以简单明了的方式获取结果。当SOQL嵌入到Apex中时,它被称为内联SOQL。

要在您的Apex代码中包含SOQL查询,请将SOQL语句包装在方括号中,并将返回值分配给一个sObjects数组。例如,以下内容将检索具有两个字段(名称和电话号码)的所有客户记录,并返回一个Account sObjects数组。

Account[] accts = [SELECT Name,Phone FROM Account];

先决条件

本单位的一些查询希望组织拥有客户和联系人。在运行查询之前,请创建一些示例数据。

  1. 在开发人员控制台中,从“调试”菜单中打开“执行匿名”窗口。
  2. 在窗口中插入下面的代码片段,然后点击执行。
// 添加客户和相关联系人
Account acct = new Account(
    Name='SFDC Computing',
    Phone='(415)555-1212',
    NumberOfEmployees=50,
    BillingCity='San Francisco');
insert acct;

// 一旦插入客户,sObject将填充一个ID。
// 获取这个ID。
ID acctID = acct.ID;

// 添加一个联系人到这个客户。
Contact con = new Contact(
    FirstName='Carol',
    LastName='Ruiz',
    Phone='(415)555-1212',
    Department='Wingo',
    AccountId=acctID);
insert con;

// 添加没有联系人的客户
Account acct2 = new Account(
    Name='The SFDC Query Man',
    Phone='(310)555-1213',
    NumberOfEmployees=50,
    BillingCity='Los Angeles',
    Description='Expert in wing technologies.');
insert acct2;

使用查询编辑器

开发者控制台提供查询编辑器控制台,使您可以运行您的SOQL查询和查看结果。查询编辑器提供了一种快速检查数据库的方法。在将它们添加到您的Apex代码之前,测试您的SOQL查询是一个好方法。当您使用查询编辑器时,您只需提供SOQL语句,而不包含围绕它的Apex代码。

让我们尝试运行以下SOQL示例:

  1. 在开发者控制台中,点击 Query Editor 标签。
  2. 将以下内容复制并粘贴到查询编辑器下的第一个框中,然后单击 Execute.
SELECT Name,Phone FROM Account

您的组织中的所有客户记录都以查询结果部分显示为包含字段的行。

基本的SOQL语法

这是一个基本的SOQL查询语法:

SELECT fields FROM ObjectName [WHERE Condition]
WHERE子句是可选的。我们从一个非常简单的查询开始。例如,以下查询检索客户并为每个客户获取两个字段:ID和电话。
SELECT Name,Phone FROM Account

查询有两部分:

  1. 选择名称,电话:这部分列出您想要检索的字段。这些字段在逗号分隔列表中的SELECT关键字之后指定。或者您只能指定一个字段,在这种情况下不需要逗号(例如SELECT Phone)。
  2. FROM客户:这部分指定您要检索的标准或自定义对象。在这个例子中,它是客户。对于名为Invoice_Statement的自定义对象,它是Invoice_Statement__c。

超越基础

与其他SQL语言不同,您不能为所有字段指定*。你必须指定你想明确得到的每个字段。如果您尝试访问您在SELECT子句中未指定的字段,则会出现错误,因为该字段尚未被检索到。

您不需要在查询中指定Id字段,因为它始终在Apex查询中返回,无论是在查询中指定还是不在查询中指定。例如:SELECT Id,Phone FROM Account和SELECT Phone FROM Account是等价的语句。唯一一次你可能想要指定Id字段,如果它是唯一的字段,你正在检索,因为你必须列出至少一个字段:SELECT ID FROM Account。在查询编辑器中运行查询时,您可能还需要指定Id字段,因为除非指定,否则ID字段将不会显示。

使用条件过滤查询结果

如果您在组织中拥有多个客户,则将全部退回。如果要限制返回给满足特定条件的客户的客户,则可以在WHERE子句中添加此条件。以下示例仅检索名称为SFDC Computing的客户。请注意,字符串比较不区分大小写。

SELECT Name,Phone FROM Account WHERE Name='SFDC Computing'
WHERE子句可以包含多个使用逻辑运算符(AND,OR)和括号分组的条件。例如,此查询将返回名称为SFDC Computing的拥有超过25名员工的所有客户:
SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' AND NumberOfEmployees>25)
这是一个更复杂条件的例子。此查询将返回所有SFDC计算客户,或者拥有超过25名员工的帐单城市为洛杉矶的所有客户。
SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' OR (NumberOfEmployees>25 AND BillingCity='Los Angeles'))

超越基础

您可以使用LIKE运算符执行模糊匹配,而不是使用相等运算符(=)进行比较。例如,您可以使用以下条件检索名称以SFDC开头的所有客户:WHERE Name LIKE’SFDC%’。 %通配符匹配任何字符或不匹配。相反的_字符可以用来匹配一个字符。

订购查询结果

执行查询时,它将从Salesforce返回记录,但不会按特定顺序进行,因此每次运行查询时都不能依赖返回数组中记录的顺序。但是,您可以选择通过添加ORDER BY子句并指定记录集应该排序的字段来对返回的记录集进行排序。本示例根据“名称”字段对所有检索到的客户进行排序。

SELECT Name,Phone FROM Account ORDER BY Name
默认排序顺序按照字母顺序排列,指定为ASC。以前的声明相当于:
SELECT Name,Phone FROM Account ORDER BY Name ASC
要反转订单,请按降序使用DESC关键字:
SELECT Name,Phone FROM Account ORDER BY Name DESC

您可以对大多数字段进行排序,包括数字和文本字段。你不能在丰富的文本和多选择选项列表上排序。

在“查询编辑器”中尝试使用这些SOQL语句,并根据“名称”字段查看返回记录的顺序如何变化。

限制返回的记录数

您可以通过添加LIMIT n子句来限制返回到任意数字的记录数,其中n是要返回的记录数。当你不关心哪些记录被返回时,限制结果集很方便,但是你只想使用记录的一个子集。例如,此查询检索返回的第一个客户。请注意,使用LIMIT 1时,返回的值是一个客户,而不是数组。

Account oneAccountOnly = [SELECT Name,Phone FROM Account LIMIT 1];

把所有的东西结合在一起

您可以按照以下顺序在一个查询中组合可选子句

SELECT Name,Phone FROM Account 
                   WHERE (Name = 'SFDC Computing' AND NumberOfEmployees>25)
                   ORDER BY Name
                   LIMIT 10
在开发人员控制台中使用“执行匿名”窗口在Apex中执行以下SOQL查询。然后检查调试日志中的调试语句。应该返回一个样本客户。
Account[] accts = [SELECT Name,Phone FROM Account 
                   WHERE (Name='SFDC Computing' AND NumberOfEmployees>25)
                   ORDER BY Name
                   LIMIT 10];
System.debug(accts.size() + ' account(s) returned.');
// 写入所有客户数组信息
System.debug(accts);

在SOQL查询中访问变量

Apex中的SOQL语句可以引用Apex代码变量和表达式,前提是冒号(:)。在SOQL语句中使用局部变量称为绑定。

这个例子展示了如何在WHERE子句中使用targetDepartment变量。

String targetDepartment = 'Wingo';
Contact[] techContacts = [SELECT FirstName,LastName 
                          FROM Contact WHERE Department=:targetDepartment];

查询相关记录

Salesforce中的记录可以通过关系相互关联:查找关系或主 – 细节关系。例如,联系人与“客户”具有查找关系。当您创建或更新联系人时,您可以将其与一个客户相关联。与相同客户关联的联系人显示在客户页面的相关列表中。以同样的方式,您可以查看Salesforce用户界面中的相关记录,您可以在SOQL中查询相关记录。

要获取与父记录相关的子记录,请为子记录添加一个内部查询。内部查询的FROM子句针对关系名称而不是Salesforce对象名称。此示例包含一个内部查询以获取与每个返回的客户关联的所有联系人。 FROM子句指定联系人关系,这是客户与客户和联系人链接的默认关系。

SELECT Name, (SELECT LastName FROM Contacts) FROM Account WHERE Name = 'SFDC Computing'

下一个示例在Apex中嵌入示例SOQL查询,并演示如何使用sObject上的Contacts关系名称从SOQL结果中获取子记录。

Account[] acctsWithContacts = [SELECT Name, (SELECT FirstName,LastName FROM Contacts)
                               FROM Account 
                               WHERE Name = 'SFDC Computing'];
// 获取子记录
Contact[] cts = acctsWithContacts[0].Contacts;
System.debug('Name of first associated contact: ' 
             + cts[0].FirstName + ', ' + cts[0].LastName);
您可以使用点符号从子对象(联系人)到其父级(Account.Name)上的字段进行遍历。例如,以下Apex片段查询联系名字为Carol的记录,并且能够通过遍历客户和联系人之间的关系来检索Carol的相关客户的名称。
Contact[] cts = [SELECT Account.Name FROM Contact 
                 WHERE FirstName = 'Carol' AND LastName='Ruiz'];
Contact carol = cts[0];
String acctName = carol.Account.Name;
System.debug('Carol\'s account name is ' + acctName);

注意

本节中的示例基于标准对象。自定义对象也可以通过使用自定义关系链接在一起。自定义关系名称以__r后缀结尾。例如,发票声明通过Invoice_Statement__c自定义对象上的Line_Items__r关系链接到订单项。 (这些自定义对象是Force.com工作簿中使用的Warehouse模式的一部分。)

通过使用SOQL for循环查询批次中的记录

使用SOQL for循环,可以在for循环中包含SOQL查询。 SOQL查询的结果可以在循环内迭代。 SOQL for循环使用不同的方法来检索记录 – 通过调用SOAP API的查询和queryMore方法,使用高效分块来检索记录。通过使用SOQL for循环,可以避免碰到堆大小的限制。

SOQL for循环遍历由SOQL查询返回的所有sObject记录。 SOQL for循环的语法是:

for (variable : [soql_query]) {
    code_block
}
要么
for (variable_list : [soql_query]) {
    code_block
}

变量和variable_list必须与soql_query返回的sObjects类型相同。

最好使用SOQL for循环的sObject列表格式,因为循环为每个200 sObjects批次执行一次。这样做使您可以批量处理记录并批量执行DML操作,这有助于避免达到管理员限制。

insert new Account[]{new Account(Name = 'for loop 1'), 
                     new Account(Name = 'for loop 2'), 
                     new Account(Name = 'for loop 3')};

// sObject列表格式为每个返回的批记录执行一次for循环
Integer i=0;
Integer j=0;
for (Account[] tmp : [SELECT Id FROM Account WHERE Name LIKE 'for loop _']) {
    j = tmp.size();
    i++;
}
System.assertEquals(3, j); // 列表中应该包含三个名为“yyy”的客户
System.assertEquals(1, i); // 由于一个批次最多可以容纳200条记录,只有三条记录应该被返回,所以循环只能执行一次