進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

SOQL クエリの作成

学習の目的

この単元を完了すると、次のことができるようになります。
  • Apex 内に SOQL クエリを記述する。
  • 開発者コンソールのクエリエディタを使用して SOQL クエリを実行する。
  • 匿名 Apex を使用して Apex に埋め込まれた SOQL クエリを実行する。
  • 関連レコードを照会する。

SOQL クエリの作成

Salesforce からレコードを読み込むには、クエリを作成する必要があります。Salesforce では、保存したレコードの読み込みに使用できる Salesforce Object Query Language (SOQL) を提供しています。SOQL は、標準の SQL 言語と似ていますが、Lightning Platform 用にカスタマイズされています。

Apex はデータベースに保存されている Salesforce レコードへの直接アクセスが可能であるため、SOQL クエリを Apex コードに埋め込んで、簡単に結果を取得できまます。Apex に埋め込まれた SOQL は、インライン SOQL と呼ばれます。

SOQL クエリを Apex コード内に含めるには、SOQL ステートメントを角括弧でラップして、戻り値を sObject の配列に割り当てます。たとえば、次のコードでは、2 つの項目 (名前と電話) を持つすべての取引先レコードを取得し、Account sObject の配列を返します。

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

前提条件

この単元の一部のクエリでは、組織に取引先と取引先責任者があることを前提としています。クエリを実行する前に、いくつかのサンプルデータを作成します。

  1. 開発者コンソールの [Debug (デバッグ)] メニューから [Execute Anonymous (匿名実行)] ウィンドウを開きます。
  2. ウィンドウに次のスニペットを入力し、[Execute (実行)] をクリックします。
// Add account and related contact
Account acct = new Account(
    Name='SFDC Computing',
    Phone='(415)555-1212',
    NumberOfEmployees=50,
    BillingCity='San Francisco');
insert acct;

// Once the account is inserted, the sObject will be 
// populated with an ID.
// Get this ID.
ID acctID = acct.ID;

// Add a contact to this account.
Contact con = new Contact(
    FirstName='Carol',
    LastName='Ruiz',
    Phone='(415)555-1212',
    Department='Wingo',
    AccountId=acctID);
insert con;

// Add account with no contact
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 クエリを実行して結果を表示できます。クエリエディタを使用して、すばやくデータベースを調査できます。そのため、SOQL クエリを Apex コードに追加する前のテストに適しています。クエリエディタを使用するときは、前後の Apex コードなしで SOQL ステートメントのみを入力する必要があります。

次の SOQL の例を実行してみましょう。

  1. 開発者コンソールで [Query Editor (クエリエディタ)] タブをクリックします。
  2. 次のコードをコピーしてクエリエディタの下の最初のボックスに貼り付け、[Execute (実行)] をクリックします。
SELECT Name,Phone FROM Account

組織内のすべての取引先レコードが [Query Results (クエリ結果)] セクションに項目のある行として表示されます。

基本的な SOQL 構文

基本的な SOQL クエリの構文を次に示します。

SELECT fields FROM ObjectName [WHERE Condition]

WHERE 句は省略可能です。まず簡単なクエリから始めましょう。たとえば、次のクエリは取引先を取得し、各取引先の 2 つの項目 (ID と電話) を取得します。

SELECT Name,Phone FROM Account

このクエリは 2 つの部分で構成されます。

  1. SELECT Name,Phone: この部分には、取得する項目がリストされます。項目は、SELECT キーワードの後にカンマ区切りリストで指定します。または、項目を 1 つだけ指定できます。この場合、カンマは不要です (SELECT Phone など)。
  2. FROM Account: この部分では、取得する標準またはカスタムオブジェクトを指定します。この例では、Account です。Invoice_Statement という名前のカスタムオブジェクトの場合は、Invoice_Statement__c になります。

中級編

他の SQL 言語と異なり、* を指定してすべての項目を取得することはできません。取得するすべての項目を明示的に指定する必要があります。SELECT 句に指定していない項目にアクセスしようとすると、項目が取得されていないため、エラーになります。

ID 項目をクエリに指定する必要はありません。Apex クエリでは、指定されたかどうかに関係なく常に ID 項目が返されます。たとえば、SELECT Id,Phone FROM AccountSELECT Phone FROM Account は同等のステートメントです。ただし、項目は少なくとも 1 つリストしなければならないため、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 Computing の取引先すべてか、従業員数が 25 よりも多く請求先市区郡がロサンゼルスである取引先すべてを返します。

SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' OR (NumberOfEmployees>25 AND BillingCity='Los Angeles'))

中級編

比較に等号演算子 (=) を使用する代わりに、LIKE 演算子を使用してあいまい一致検索を実行することができます。たとえば、条件 WHERE Name LIKE 'SFDC%' を使用して、名前が SFDC で開始する取引先をすべて取得できます。% ワイルドカード文字は、0 個以上の文字に一致します。_ 文字を使用して 1 文字のみに一致させることもできます。

クエリ結果の順序付け

クエリが実行されると、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 を使用する場合、返される値は 1 件の取引先であり、配列ではないので注意してください。

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

句の組み合わせ

1 つのクエリ内に、省略可能な句を次の順序で組み合わせることができます。

SELECT Name,Phone FROM Account 
                   WHERE (Name = 'SFDC Computing' AND NumberOfEmployees>25)
                   ORDER BY Name
                   LIMIT 10

開発者コンソールの [Execute Anonymous (匿名実行)] ウィンドウを使用して、次の Apex 内の SOQL クエリを実行します。実行後、デバッグログでデバッグステートメントを調査します。1 件のサンプル取引先が返されます。

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.');
// Write all account array info
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 句では、Contacts リレーションを指定しています。これは、取引先と取引先責任者をリンクする Account のデフォルトのリレーションです。

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

次の例は、サンプル SOQL クエリを Apex に埋め込み、sObject に対して Contacts リレーション名を使用して、SOQL の結果から子レコードを取得する方法を示します。

Account[] acctsWithContacts = [SELECT Name, (SELECT FirstName,LastName FROM Contacts)
                               FROM Account 
                               WHERE Name = 'SFDC Computing'];
// Get child records
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 リレーションを介して品目名にリンクします

SOQL for ループを使用したレコードの一括クエリ

SOQL for ループを使用すると、for ループ内に SOQL クエリを追加できます。SOQL クエリの結果は、ループ内で反復処理できます。SOQL for ループでは、さまざまなメソッドを使用してレコードを取得します。レコードは、SOAP API の query および queryMore メソッドへのコールで効率のよいチャンク分割を使用して取得されます。SOQL for ループを使用することで、ヒープサイズ制限に達するのを回避できます。

SOQLfor ループは SOQL クエリで返されたすべての sObject レコードを反復します。SOQLfor ループの構文は次のいずれかになります。
for (variable : [soql_query]) {
    code_block
}
または
for (variable_list : [soql_query]) {
    code_block
}

variable および variable_list は、soql_query で返される sObject と同じデータ型である必要があります。

ループは、200 個の sObject のバッチごとに 1 回実行されるため、SOQL for ループの sObject リスト形式を使用することをお勧めします。そうすることで、レコードのバッチに対する処理や DML 操作の一括実行が可能になり、ガバナ制限に達するのを回避できます。

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

// The sObject list format executes the for loop once per returned batch
// of records
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); // The list should have contained the three accounts
                       // named 'yyy'
System.assertEquals(1, i); // Since a single batch can hold up to 200 records and,
                       // only three records should have been returned, the 
                       // loop should have executed only once
retargeting