【図解】Salesforce フローのループ内で更新してはいけない理由とコレクション変数の使い方

このページには広告リンクを含みます。

ループの中のレコード更新で止まる仕組み

Salesforce は基盤を複数の組織で共有しているため、1トランザクションで使える量に上限(ガバナ制限)を設けています。ループが関わる主な数字はこの4つです。

上限 値 消費する要素 ループの中に置くと
SOQL の回数 100回 レコードを取得、絞り込み条件つきの更新・削除 件数分増える
取得行 50,000行 レコードを取得 重複して取ると早く尽きる
DML の回数 150回 レコードを作成・更新・削除 件数分増える
DML の対象行 10,000行 レコードを作成・更新・削除 外に出しても変わらない

要素1つにつき1回というのが数え方の基本です。要素が1つでも、ループの中にあれば周回の数だけ実行されます。

例として、取引先の更新で動くフローを考えます。取引先責任者を取得し、ループで1件ずつレコードを更新します。取引先責任者が30件の取引先なら DML は30回で通ります。200件の取引先では、151件目の更新で止まります。テストデータが数件だと気付かないのは、このためです。

実際にはもっと手前で止まることもあります。同じトランザクションで動く他のフローや Apex トリガーも、同じ枠を使うからです。

失敗すると、障害パスを付けていてもトランザクション全体がロールバックされます。つまり取引先の更新そのものが取り消され、ユーザーの画面にはエラーが出ます。

コレクション変数と割り当てで直す手順

直し方は決まっています。Trailhead の説明は「取得はループの前、書き込みはループの後」です。

ループの中でレコードを更新して151回目に止まる設計と、コレクションに集めてループの外で1回更新する設計を上下に比べた図
同じ200件でも、DMLの回数は200回と1回に分かれる
  1. レコードを取得:ループの前で、必要なレコードをまとめて取ります。
  2. リソースを作る:レコード型で「複数の値を許可」にした変数(レコードのコレクション変数)を用意します。名前は UpdatedContacts のように中身が分かるものにします。
  3. ループ:取得したコレクションを渡します。
  4. 割り当て(1):現在の項目(Current Item)の項目に、新しい値を入れます。
  5. 割り当て(2):演算子「追加」(Add)で、現在の項目をコレクション変数に足します。1つの割り当て要素に2行まとめても構いません。
  6. レコードを更新:ループの「最後の項目の後」(After Last)の線に置き、コレクション変数を渡します。

コレクションへの追加は変数の操作なので、DML を消費しません。書き込みはループの後の1回だけです。

手順5を忘れると、更新要素に空のコレクションが渡ります。現在の項目の中身は次の周回で入れ替わるため、そこに入れた値は追加しない限り残りません。

SOQL 101 エラーも同じ原因

Too many SOQL queries: 101 は、1トランザクションで SOQL を100回より多く使ったときのエラーです。フローでは次の置き方が原因になります。

  • ループの中のレコードを取得
  • ループの中の、絞り込み条件で対象を指定するレコードを更新・削除(この形は SOQL と DML の両方を1回ずつ使う)
  • 取得が多いサブフローをループの中で呼ぶ

Salesforce の数え方では、絞り込み条件つきの更新要素も問い合わせを1回使います。ループの中で親を探して更新する形は、両方の枠を同時に減らすことになります。

Flow が自動でまとめる範囲と、まとめない範囲

「フローは自動でバルク化される」と聞いて、ループも大丈夫だと考えるのは間違いです。自動でまとまるのは、別々のインタビューが同じ要素に着いたときだけです。

別々のインタビューが同じ要素に着くとまとめられるが、1つのインタビュー内のループはまとめられないことを示す図
Flow が自動でまとめるのはインタビューをまたぐ操作だけ
場面 例 DML の回数
複数のインタビューが同じ要素に着く データローダで100件の商談を更新し、100のインタビューが同じレコードを更新に到達 まとめて1回
1つのインタビューの中のループ 1件の取引先の変更で動き、ループで100件を1件ずつ更新 100回
1つのインタビューでループの外から更新 同じ処理をコレクションで1回更新 1回

大量取り込みは別の話として考えておきます。判断ガイドの例では、20,000件を200件ずつ取り込むと、トリガーは100回起動します。1件ずつの保存で問題がなくても、200件単位で動いたときの消費は別に確かめておく必要があります。

外に出しても残る上限

コレクションで DML を1回にしても、対象行の上限は残ります。さらに更新したレコードの自動化も同じトランザクションで動きます。

  • 対象を絞る:Trailhead の例では、全取引先責任者のチェックを外すスケジュールフローが10,000件を超えて失敗します。レコードを更新の条件で、チェックが付いているレコードだけを対象にすれば件数を抑えられます。同じ絞り込みは、開始要素とレコードを取得にも設定できます。
  • トランザクションを分ける:待機要素を挟むと新しいトランザクションが始まります。
  • 印を付けて後で処理する:判断ガイドは、即時でなくてよい大量の処理について、トリガーでレコードに印を付け、スケジュールジョブでまとめて処理する方法を示しています。
  • Apex に任せる:突き合わせが複雑、件数が数千を超える、途中で取り消す制御が要る場合です。呼び出し可能な Apex でフローから呼ぶ方法もあります。

項目の写し替えが目的なら、変換要素(Transform)でループ自体を減らせる場合もあります。

公開前に確かめること

本番で止めないためのチェックです。編集部のおすすめとして挙げます。

  • テストデータに、1件の親に子が200件以上あるパターンを入れる
  • データローダなどで200件を一度に更新してみる
  • ループの線の中に、レコードの取得・作成・更新・削除とサブフローがないか目で見る
  • 同じオブジェクトで動く他のフローや Apex トリガーの消費も足して考える

Salesforce Code Analyzer のフロー用ルールも使えます。パイプラインに組み込むと、人の目での見落としを減らせます。

関連記事

よくある質問

Salesforce フローのループの中でレコードを更新してはいけないのはなぜですか?
ループの中のレコードを更新要素は1件ごとに1回の DML を使うためです。上限は1トランザクション150回なので、151回目で失敗し、トランザクション全体が取り消されます。
ループ内の更新はどう直せばよいですか?
ループの中では割り当て要素で値を変え、レコードのコレクション変数に追加します。ループを抜けた後にレコードを更新要素を1つ置き、コレクションをまとめて渡せば、DML は1回で済みます。
フローで Too many SOQL queries: 101 が出る原因は何ですか?
1トランザクションで SOQL を100回より多く使ったときに出るエラーです。ループの中のレコードを取得要素や、絞り込み条件つきの更新・削除要素が主な原因で、同じトランザクションの他の自動化の分も合算されます。
レコードが1万件を超える場合はフローで更新できますか?
DML の対象は1トランザクション10,000行までなので、コレクションでまとめても1回で10,000行を超える更新はできません。絞り込みで対象を減らす、処理を分ける、Apex のバッチを使うなどを検討します。