参照関係の親レコードへApexで商談数を集計する書き方
積み上げ集計項目が使えない場面で、GROUP BYと差分更新を使ったApexの集計処理を説明します。
標準の積み上げ集計でできるか先に確かめる
親子の件数や合計を親へ反映したいとき、まず確かめることがあります。積み上げ集計項目(Roll-Up Summary)が使えるのは、主従関係(マスタ詳細関係)でつながった親子だけです。参照関係(ルックアップ)でつながった親子には使えません。
積み上げ集計項目でできるのは、件数(COUNT)、合計(SUM)、最小値・最大値(MIN・MAX)の4種類です。使える条件がそろっているなら、Apexを書くより積み上げ集計項目を作るほうが早く、保守もかかりません。
⚠️ 一度、積み上げ集計項目をどれか1つでも作ると、その主従関係を参照関係へ変更できなくなります。
積み上げ集計が使えないときの選択肢
商談から取引先責任者への参照は、標準では主従関係になりません。カスタムの参照関係項目(ルックアップ)で商談から取引先責任者を指している場合、積み上げ集計項目は使えないので、Apexで集計して書き戻します。
項目を用意する
商談に参照関係項目を作る
商談オブジェクトに、取引先責任者を指す参照関係項目(例:
Contact__c)を作成します。取引先責任者に集計結果を格納する項目を作る
取引先責任者オブジェクトに、数値項目(例:
OppCounts__c)を作成します。
集計をApexで行う
取引先責任者ごとに商談を1件ずつ数える代わりに、SOQLの集計関数で最初から件数を取ります。Apex側でMapに数え上げるより、クエリで返る行数そのものが少なくなります。
public with sharing class ContactOpportunityRollup { public static void recalculate() { Map<Id, Integer> countsByContactId = new Map<Id, Integer>(); for (AggregateResult ar : [ SELECT Contact__c contactId, COUNT(Id) oppCount FROM Opportunity WHERE Contact__c != null GROUP BY Contact__c ]) { countsByContactId.put((Id) ar.get('contactId'), (Integer) ar.get('oppCount')); } List<Contact> targets = [ SELECT Id, OppCounts__c FROM Contact WHERE Id IN :countsByContactId.keySet() OR OppCounts__c != null ]; List<Contact> toUpdate = new List<Contact>(); for (Contact con : targets) { Integer count = countsByContactId.containsKey(con.Id) ? countsByContactId.get(con.Id) : 0; if (con.OppCounts__c != count) { con.OppCounts__c = count; toUpdate.add(con); } } if (!toUpdate.isEmpty()) { Database.update(toUpdate, false); } } }
GROUP BYで取引先責任者ごとの商談数を1回のSOQLで取得します。ループの中にSOQLはありません。
差分だけを更新する
商談を持つ取引先責任者だけをMapへ集計し、その分だけupdateする書き方だと、商談がすべて削除されて0件になった取引先責任者の数値が、古い値のまま残ります。上のコードでは、OppCounts__cに値が入っている取引先責任者もクエリに含め、集計結果と食い違っている行だけを更新対象にしています。値が変わらない行はDMLに含めないので、対象件数も抑えられます。
動作確認
取引先責任者を作成する
集計対象のレコードを用意します。
商談を複数件作成する
作成した取引先責任者を参照する商談を、2件以上作ります。
開発者コンソールで実行する
Execute Anonymous Windowで
ContactOpportunityRollup.recalculate();を実行します。取引先責任者の項目を確認する
OppCounts__cが商談の件数と一致していることを確認します。商談を1件削除してから再実行し、件数が減ることも確認します。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
| ルックアップでつながった親子に積み上げ集計項目を作ろうとする | 選択肢に出てきません。使えるのは主従関係だけです |
| 商談を1件ずつクエリして数える | 取引先責任者の数だけSOQLを発行することになり、件数が増えた日に上限へ当たります |
| 集計結果がある取引先責任者だけを更新対象にする | 商談が0件に戻った取引先責任者の数値が、古いまま残ります |
大量データを一括でrecalculateする | 対象が多い組織では、1回のトランザクションのDML行数上限に当たります。Database.Batchableに分けて処理してください |
確認した環境
- 2026年9月/Salesforce Summer '26(APIバージョン67.0)時点の公式ヘルプで、積み上げ集計項目が主従関係でのみ使えること、対応する集計種別(COUNT・SUM・MIN・MAX)を確認しています
まとめ
- 積み上げ集計項目が使えるのは主従関係の親子だけです。ルックアップでつながった親子には使えません
- 使える条件がそろっているなら、Apexより積み上げ集計項目を作るほうが保守がかかりません
- Apexで集計するときは、取引先責任者ごとにクエリを発行せず、GROUP BYで1回にまとめます
- 集計が0件になったレコードも更新対象に含めないと、古い数値が残ります
- 対象件数が多い組織では、1トランザクションのDML行数上限に当たるので
Database.Batchableに分けます
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。