ガバナ実行制限内での Apex の実行
Apex コードは、これらの事前定義された実行制限内で実行する必要があります。ガバナ制限を超えると、処理できない実行時例外が発生します。コードで次のベストプラクティスに従うことで、この制限に達するのを回避できます。たとえば、100 枚の T シャツを洗わなければならないとします。T シャツを 1 枚ずつ、つまり 1 回の洗濯で 1 枚ずつ洗いますか、または何枚かずつまとめて数回の洗濯ですむようにしますか。クラウドでのコーディングの利点は、より効率的なコードを書いて、消費するリソースを減らす方法を学ぶことにあります。
ガバナ実行制限は、トランザクション単位で適用されます。たとえば、1 つのトランザクションは SOQL クエリを最大 100 回、DML ステートメントを最大 150 回発行できます。一度にキューに入れることができるか、有効にできる一括処理ジョブの数など、こうしたトランザクション単位の制限が適用されない方法もあります。
特定のガバナ制限を超えないコードを記述するためには、次に示すいくつかのベストプラクティスがあります。
DML コールを一括処理する
個々の sObject ではなく、sObject のリストに対して DML コールを行うと、DML ステートメント制限に達する可能性が低くなります。次の最初の例は、DML 操作を一括処理しないコール方法です。その次の例は、推奨される DML ステートメントのコール方法です。
例: 個々の sObject に対する DML コール
for ループが、liList List 変数に含まれる品目名を反復処理します。品目名ごとに、Description__c 項目に新しい値を設定し、品目名を更新します。リストに 150 を超える品目が含まれる場合、151 回目の update コールは、DML ステートメント制限の 150 を超えるため実行時例外を返します。これをどう修復すればよいでしょうか。2 つ目の例は、単純な解決策です。
1for(Line_Item__c li : liList) {
2 if (li.Units_Sold__c > 10) {
3 li.Description__c = 'New description';
4 }
5 // Not a good practice since governor limits might be hit.
6 update li;
7}推奨される代替方法: sObject リストに対する DML コール
この拡張バージョンの DML コールは、更新された品目名を含むリスト全体に対して更新を実行します。まず、新しいリストを作成し、次にループ内ですべての更新品目名を新しいリストに追加します。続いて、新しいリストに対して一括更新を実行します。
1List<Line_Item__c> updatedList = new List<Line_Item__c>();
2
3for(Line_Item__c li : liList) {
4 if (li.Units_Sold__c > 10) {
5 li.Description__c = 'New description';
6 updatedList.add(li);
7 }
8}
9
10// Once DML call for the entire list of line items
11update updatedList;より効率的な SOQL クエリ
SOQL クエリを for ループブロック内に置くと、SOQL クエリが反復ごとに実行されて、100 回というトランザクションあたりの SOQL クエリ数制限を超える可能性があるため、よい方法とはいえません。次の最初の例では、SOQL クエリを Trigger.new の品目ごとに実行するため、非効率的です。代替方法の例では、SOQL クエリを 1 回だけ使用して子品目を取得するクエリに変更されています。
例: 非効率的な子品目のクエリ
この例の for ループは、Trigger.new に含まれるすべての請求書明細を反復処理します。ループ内で実行される SOQL クエリは、各請求書明細の子品目名を取得します。100 件を超える請求書明細が挿入または更新されて Trigger.new に含まれている場合、SOQL 制限に達するため、実行時例外が発生します。2 つ目の例では、1 回だけコールできる別の SOQL クエリを作成してこの問題を解決しています。
1trigger LimitExample on Invoice_Statement__c (before insert, before update) {
2 for(Invoice_Statement__c inv : Trigger.new) {
3 // This SOQL query executes once for each item in Trigger.new.
4 // It gets the line items for each invoice statement.
5 List<Line_Item__c> liList = [SELECT Id,Units_Sold__c,Merchandise__c
6 FROM Line_Item__c
7 WHERE Invoice_Statement__c = :inv.Id];
8 for(Line_Item__c li : liList) {
9 // Do something
10 }
11 }
12}推奨される代替方法: SOQL クエリを 1 回のみ使用した子品目のクエリ
この例では、品目ごとに SOQL クエリをコールするという問題を回避しています。変更された SOQL クエリでは、Trigger.new に含まれるすべての請求書明細を取得し、さらにネストしたクエリでその品目名を取得します。この方法では SOQL クエリが 1 回だけ実行され、制限内に収まっています。
1trigger EnhancedLimitExample on Invoice_Statement__c (before insert, before update) {
2 // Perform SOQL query outside of the for loop.
3 // This SOQL query runs once for all items in Trigger.new.
4 List<Invoice_Statement__c> invoicesWithLineItems =
5 [SELECT Id,Description__c,(SELECT Id,Units_Sold__c,Merchandise__c from Line_Items__r)
6 FROM Invoice_Statement__c WHERE Id IN :Trigger.newMap.KeySet()];
7
8 for(Invoice_Statement__c inv : invoicesWithLineItems) {
9 for(Line_Item__c li : inv.Line_Items__r) {
10 // Do something
11 }
12 }
13}SOQL for ループ
レコードに対して 200 件のバッチ単位で操作を行うには、SOQL for ループを使用します。これにより、6 MB のヒープサイズ制限を回避できます。この制限は、同期して実行されるコードに対するもので、非同期のコード実行では制限がより厳しくなります。
例: for ループを使用しないクエリ
次の例の SOQL クエリでは、すべての商品品目を取得し、List 変数に保存します。返された商品品目のサイズが大きく、大量のレコードが返された場合、ヒープサイズ制限に達する可能性があります。
1List<Merchandise__c> ml = [SELECT Id,Name FROM Merchandise__c];推奨される代替方法: for ループを使用したクエリ
このヒープサイズ制限を回避するには、2 つ目のバージョンで SOQL for ループを使用し、返された結果を 200 レコードのバッチ単位で反復処理します。これにより、ml List 変数が、クエリ結果のすべての品目名ではなく 200 品目を保持するため、サイズが小さくなり、また、バッチごとに再作成されます。
1for (List<Merchandise__c> ml : [SELECT Id,Name FROM Merchandise__c]){
2 // Do something.
3}