0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Apex実装のベストプラクティス(後編)

Last updated at Posted at 2025-02-25

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'
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?