この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

SOQL For ループ

SOQL for ループは SOQL クエリで返されたすべての sObject レコードを反復します。
SOQL for ループの構文は次のいずれかになります。
1for (variable : [soql_query]) {
2    code_block
3}
または
1for (variable_list : [soql_query]) {
2    code_block
3}
variable および variable_list は、soql_query で返される sObject と同じデータ型である必要があります。標準 SOQL クエリと同様、[soql_query] ステートメントは、: 構文を使用して WHERE 句のコード式を参照することができます。次に例を示します。
1String s = 'Acme';
2for (Account a : [SELECT Id, Name from Account
3                  where Name LIKE :(s+'%')]) {
4    // Your code
5}
次の例では、SOQL クエリからのリストの作成と DML update メソッドを結合します。
1// Create a list of account records from a SOQL query
2List<Account> accs = [SELECT Id, Name FROM Account WHERE Name = 'Siebel']; 
3
4// Loop through the list and update the Name field
5for(Account a : accs){
6   a.Name = 'Oracle';
7}
8
9// Update the database
10update accs;

SOQL For ループと標準 SOQL クエリの比較

SOQL for ループは、sObjects を取得するために使用するメソッドが、標準 SOQL ステートメントとは異なります。「SOQL および SOSL クエリ」で説明する標準クエリはクエリの count または多数のオブジェクトレコードを取得できますが、SOQL for ループは、SOAP API の query メソッドと queryMore メソッドのコールで効率的なチャンクを使用して、すべての sObject を取得します。開発者は常に SOQL for ループを使用して、多数のレコードを返すクエリ結果を処理し、ヒープサイズの制限に達するのを回避します。

集計関数を含むクエリでは、queryMore をサポートしません。for ループで 2,000 を超える行を返す集計関数を含むクエリを使用すると、実行時例外が発生します。

SOQL For ループの形式

SOQL for ループは、単一の sObject 変数を使用して一度に 1 件のレコードを処理するか、sObject リストを使用して一度に 200 個の sObject を一括処理できます。
  • 単一の sObject 形式は for ループの <code_block> を sObject レコードごとに 1 回実行します。そのため、理解しやすく、簡単に使用できますが、for ループの本文内でデータ操作言語 (DML) ステートメントを使用すると、効率性が大幅に低下します。DML ステートメントは、一度に 1 つの sObject の処理のみを完了します。
  • sObject リスト形式は for ループの <code_block> を 200 件の sObject のリストごとに 1 回実行します。そのため、多少理解しにくく、使用が難しくなりますが、for ループの本文内で DML ステートメントを使用する必要がある場合に最適です。DML ステートメントは、sObject のリストを一括処理します。
たとえば、次のコードは 2 種類の SOQL クエリ for ループの差異を示します。
1// Create a savepoint because the data should not be committed to the database
2Savepoint sp = Database.setSavepoint(); 
3
4insert new Account[]{new Account(Name = 'yyy'), 
5                     new Account(Name = 'yyy'), 
6                     new Account(Name = 'yyy')};
7
8// The single sObject format executes the for loop once per returned record
9Integer i = 0;
10for (Account tmp : [SELECT Id FROM Account WHERE Name = 'yyy']) {
11    i++;
12}
13System.assert(i == 3); // Since there were three accounts named 'yyy' in the
14                       // database, the loop executed three times
15
16// The sObject list format executes the for loop once per returned batch
17// of records
18i = 0;
19Integer j;
20for (Account[] tmp : [SELECT Id FROM Account WHERE Name = 'yyy']) {
21    j = tmp.size();
22    i++;
23}
24System.assert(j == 3); // The list should have contained the three accounts
25                       // named 'yyy'
26System.assert(i == 1); // Since a single batch can hold up to 200 records and,
27                       // only three records should have been returned, the 
28                       // loop should have executed only once
29
30// Revert the database to the original state
31Database.rollback(sp);
  • break キーワードと continue キーワードは、どちらのインラインクエリ for ループ形式でも使用できます。sObject リスト形式を使用すると、continue は、sObjects の次のリストにスキップします。
  • DML ステートメントは一度に最大 10,000 件のレコードを処理でき、sObject リスト for ループは 200 件のレコードを一括処理します。そのため、sObject リスト for ループで返されたレコードごとに複数のレコードを挿入、更新、または削除する場合、制限のランタイムエラーが発生する可能性があります。「実行ガバナと制限」を参照してください。
  • SOQL for ループで QueryException が発生し、「Aggregate query has too many rows for direct assignment, use FOR loop」というメッセージが表示される可能性があります。この例外は、取得された sObject の大量の子レコード (200 以上) にループ内でアクセスする場合や、このようなレコードセットのサイズを取得する場合に発生することがあります。たとえば、次の SOQL for ループのクエリは、特定の取引先の子取引先責任者を取得します。この取引先に含まれる子取引先責任者が 200 を超える場合は、for ループのステートメントによって例外が発生します。
    1for (Account acct : [SELECT Id, Name, (SELECT Id, Name FROM Contacts) 
    2                    FROM Account WHERE Id IN ('<ID value>')]) { 
    3    List<Contact> contactList = acct.Contacts; // Causes an error
    4    Integer count = acct.Contacts.size(); // Causes an error
    5}
この例外を回避するには、次のように for ループを使用して、子レコードを反復処理します。
1for (Account acct : [SELECT Id, Name, (SELECT Id, Name FROM Contacts) 
2                    FROM Account WHERE Id IN ('<ID value>')]) { 
3    Integer count=0;
4    for (Contact c : acct.Contacts) {
5        count++;
6    }
7}

メモ