Apexはガバナ制限(Governor Limits) の影響を受けるため、効率的なデータ処理をしないと パフォーマンスの低下やエラー に繋がります。
今回は、データセットの最適化、SOQL for ループの活用、SOQLのパフォーマンス向上のヒント について、具体的なコード例を交えて整理しました。
4. 原則: データセットを減らす
✅ 理由
- 不必要なレコードを処理しないことで、メモリ使用量(ヒープサイズ)を削減できる。
Trigger.oldMap
を使用することで、実際に変更されたレコードのみ処理できる。- SOQLクエリのフィルタリングを活用し、取得するレコードを最小限に抑える。
📌 例:Trigger.oldMap を使用して、不要なレコードを処理対象から除外
trigger AccountTrigger on Account (before update) {
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : Trigger.new) {
Account oldAcc = Trigger.oldMap.get(acc.Id);
// ❌ 名前が変更されていない場合は処理しない
if (acc.Name == oldAcc.Name) {
continue; // 変更なし → スキップ
}
acc.Description = 'Updated';
accountsToUpdate.add(acc);
}
if (!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
}
}
✨ 修正のポイント
-
Trigger.oldMap.get(acc.Id)
を使い、変更前後の値を比較。 - 変更がないレコードはスキップして、不要なDML操作を減らす。
📌 例:SOQLクエリをフィルタリング
List<Contact> contacts = [SELECT Id, LastName FROM Contact WHERE IsDeleted = FALSE AND LastName != NULL];
✅ 不要なデータ(削除済み・NULLの値)を除外し、取得するレコードを最小化。
5. 原則: SOQL for ループを使用してヒープサイズを減らす
✅ 理由
- 大量データを処理するとき、SOQLの結果をすべてメモリに読み込むと「ヒープサイズ制限」に達する可能性がある。
- SOQL for ループを使用すると、少量ずつレコードを処理できるため、メモリ使用量を抑えられる。
📌 例:❌ 悪い例(ヒープサイズが大きくなる可能性)
List<Account> accounts = [SELECT Id, Name FROM Account];
for (Account acc : accounts) {
System.debug(acc.Name);
}
⛔ 問題点
- 全レコードをリストに格納するため、ヒープサイズの制限(6MB)を超えるリスクがある。
📌 ✅ 良い例(SOQL for ループを使用)
for (Account acc : [SELECT Id, Name FROM Account]) {
System.debug(acc.Name);
}
✅ SOQL for ループを使用することで、Apex が少量ずつレコードを処理し、メモリ使用量を削減できる!
📌 例:SOQL for ループを使って、DML操作を最適化
List<Contact> contactsToUpdate = new List<Contact>();
for (Contact con : [SELECT Id, LastName FROM Contact WHERE LastName != NULL]) {
con.LastName = con.LastName + ' Updated';
contactsToUpdate.add(con);
// バッチサイズ(200件)ごとにDML実行
if (contactsToUpdate.size() == 200) {
update contactsToUpdate;
contactsToUpdate.clear();
}
}
// 残りのレコードを更新
if (!contactsToUpdate.isEmpty()) {
update contactsToUpdate;
}
✅ 大量データを処理するときは、バッチサイズを管理しながらDMLを実行すると効率的!
6. SOQL のパフォーマンス向上のためのヒント
✅ 理由
- SOQLの検索速度を向上させるためには、インデックスを活用するのが重要。
- Salesforceのインデックスが設定されている項目(Id, 外部ID)を使うとクエリのパフォーマンスが向上。
📌 例:❌ 悪い例(インデックスなしのカラムをWHERE条件に使用)
List<Contact> contacts = [SELECT Id FROM Contact WHERE LastName = 'Tanaka'];
⛔ 問題点
-
LastName
は通常インデックスが設定されていないため、検索速度が遅くなる。
📌 ✅ 良い例(インデックスを使用する)
List<Contact> contacts = [SELECT Id FROM Contact WHERE Id IN :contactIds];
✅ Id
は標準でインデックスが付いているため、高速検索が可能!
📌 例:外部IDを活用してクエリを最適化
List<Account> accounts = [SELECT Id FROM Account WHERE External_Id__c = 'A00123'];
✅ 外部ID(External_Id__c
)にインデックスを設定すると、高速検索が可能!
📌 例:カスタムインデックスを活用
- 標準のインデックスがない項目(
Status__c
など)でも、Salesforceにインデックスをリクエストしてパフォーマンスを改善できる。
List<Case> cases = [SELECT Id FROM Case WHERE Status__c = 'Open']; // カスタムインデックスをリクエスト可能
🎯 まとめ
項目 | ❌ 悪い例 | ✅ 良い例 |
---|---|---|
データセットの削減 | すべてのレコードを処理 |
Trigger.oldMap で変更があるレコードのみ処理 |
SOQL for ループ | リストに全件取得 |
for (record : [SOQL]) を使う |
インデックス活用 | WHERE LastName = 'Tanaka' |
WHERE Id IN :setIds を使う |
外部IDの活用 | WHERE Email = 'test@example.com' |
WHERE External_Id__c = 'A00123' |