flow-formula-and-expression-patterns
Flowでフォーラに対して、NULL安全性とデータ型の正確性、パフォーマンスを意識した数式リソースおよび条件式を作成できます。遅延評価、BLANKVALUE/ISBLANKガード、ISPICKVALと=の使い分け、VALUE/TEXT/DAETIMEVALUEの型変換、TODAY/NOWの時間帯の違い、および5,000文字の数式上限について対応します。オブジェクトのレコードレベルの数式フィールドは対象外となります(admin/formula-fieldsを参照)。検証ルールの数式も対象外です。異なるランタイムコンテキストで動作するため別途確認してください(admin/validation-rulesを参照)。
description の原文を見る
Author NULL-safe, type-correct, performance-aware Formula resources and condition expressions in Flow: lazy re-evaluation, BLANKVALUE/ISBLANK guards, ISPICKVAL vs =, VALUE/TEXT/DATETIMEVALUE coercion, time-zone differences between TODAY/NOW, and the 5,000-character formula limit. NOT for record-level formula fields on objects — see admin/formula-fields. NOT for Validation Rule formulas (different runtime context) — see admin/validation-rules.
SKILL.md 本文
Flow フォーミュラと式のパターン
Flow フォーミュラリソース、Decision の条件式、またはスクリーンコンポーネントのフォーミュラプロパティを作成または確認する際に有効にします。このスキルは、NULL安全ラッピング、正しい型の強制変換、遅延再評価の認識、および 5,000 文字の上限を強制します。これらに違反すると、実行時にサイレント NULL、「Comparison value cannot be null」決定エラー、ループ内での反復ごとのパフォーマンスの悪化として現れます。
開始前に
Flow でフォーミュラを作成または修正する前に、以下のコンテキストを把握してください。
- フォーミュラは何型を返しますか? Boolean、Text、Number、Currency、Date、DateTime、Time。Flow は暗黙的な戻り値型の変更を拒否します。強制変換は明示的でなければなりません(
VALUE()、TEXT()、DATEVALUE()、DATETIMEVALUE())。 - 入力の型は何で、どれが null 値の可能性がありますか?
null入力は、算術演算子、比較演算子、論理演算子を通じてnull出力に伝播します(NULL + 1 = NULL; NULL && TRUE = NULL)。null の可能性がある入力はBLANKVALUE(field, default)またはIF(ISBLANK(field), default, field)でラップしてください。 - フォーミュラは Loop 本体内で参照されるか、3 つ以上の要素で参照されますか?
{!FormulaResourceName}への各参照は、式全体を再評価します。200 回のイテレーションループ内で 10 回参照されるフォーミュラは 2,000 回実行されます。フォーミュラが非自明の場合は、Assignment でキャッシュしてください。 - フォーミュラが 5,000 文字に近づいていますか? 単一フォーミュラの上限です。複数の構成 Formula リソースに分解してください(FormulaA は FormulaB を参照し、FormulaB は FormulaC を参照)。各レイヤーは遅延方式で再評価されます。
- これはピックリスト比較ですか?
ISPICKVAL(PicklistField__c, "Value")またはINCLUDES(MultiSelectField__c, "Value")を使用してください。リテラル文字列に対して=を使用しないでください。実行中のロケールの API 名対ラベルを比較し、サイレント false negatives の P1 ソースです。 - これは日付/時刻フォーミュラですか?
TODAY()は実行中のユーザーのローカルタイムゾーン内のローカル日付を返します。NOW()は組織のデフォルトタイムゾーンを返します。複数タイムゾーンのチームは日付の境界でエッジケースに当たります。 - フォーミュラは Decision 要素内、Formula リソース内、またはスクリーンコンポーネントプロパティ内にありますか? 同じ言語、3 つの評価コンテキストです。「Comparison value cannot be null」をスローする Decision 条件は、Formula リソースと同じ NULL ガード処理が必要です。
主要概念
概念 1: フォーミュラが表示される 3 つの場所(1 つの言語、3 つのコンテキスト)
Flow フォーミュラ言語は、「ヘルプ」→「フォーミュラ演算子と関数」に記載されている標準 Salesforce フォーミュラ言語の厳密なサブセットです。同じ構文が 3 つの異なる評価コンテキストに表示されます。
- Formula リソース — リソース内で一度宣言され、
{!FormulaResourceName}で参照されます。遅延方式:参照されるたびに評価されます。 - Decision 条件式 — 「フォーミュラが True に評価される」が選択されている場合、結果の条件行内に直接入力されるフォーミュラ。Decision 要素が実行されるときに 1 回評価されます。
- スクリーンコンポーネントフォーミュラプロパティ — テキスト表示、デフォルト値、検証、リアクティブコンポーネントバインディング内のフォーミュラ。Flow ランタイムのスクリーン描画ごとに評価されます。
3 つすべては、同じ演算子/関数ライブラリに加えて、{!$Flow.CurrentDateTime}、{!$Flow.CurrentDate}、{!$Flow.FaultMessage}、{!$Flow.InterviewStartTime}、{!$Flow.ActiveStages} などの Flow 固有のグローバルを共有します。Validation Rule フォーミュラコンテキストは同一ではありません。Validation Rules は PRIORVALUE() と ISCHANGED() を呼び出すことができますが、Flow ではできません。Workflow フィールド更新フォーミュラコンテキストは 3 番目の方言です。これも相互交換可能ではありません。
概念 2: 遅延再評価(ループ本体パフォーマンストラップ)
Flow は Formula リソースの結果をメモ化しません。{!FormulaResourceName} への参照は常に新規の評価をトリガーします。コストは累積します。
- Formula リソースが 1 回参照され、ループの外側:1 評価。コスト無し。
- Formula リソースが 1 回参照され、200 レコードをイテレーションするループ内:200 評価。通常は問題無し。
- Formula リソースが 10 回参照され、200 レコードをイテレーションするループ内:2,000 評価。目に見える CPU 使用。
- Formula リソースが他の 3 つの Formula リソースを参照し、各々が 200 回のイテレーションループで 5 回参照される場合:200 × 5 × 4 = 4,000 評価と CPU タイムガバナー違反の可能性。
対策:高コストのフォーミュラが繰り返し参照される場合、Assignment で型付き変数に 1 回評価してから、その後は変数を参照してください。Decision 要素、スクリーンコンポーネント、および下流の Assignment はすべてキャッシュされた変数を読み取ります。再評価コストはゼロです。
概念 3: NULL 伝播と明示的な型の強制変換
Salesforce フォーミュラ NULL セマンティクスは Java-like ではなく SQL-like です。
NULL + 1→NULL(1ではなく)。NULL = NULL→ 不明(実際には FALSE が多い。NULL をテストするにはISBLANK()を使用してください)。NULL && TRUE→NULL(FALSEではなく)。NULL で汚染されたフォーミュラを参照する Decision 条件は、スロー或いは予期しないデフォルト結果の評価をします。"" = NULL→ Text の場合は TRUE(空の文字列と null は Text フィールドで相互交換可能)。Number の場合は FALSE(0 は null ではなく)。
型の強制変換は明示的です。
- Text → Number:
VALUE(TextVar)。TextVarが非数値の場合、実行時にスローします。null の可能性がある入力はIF(ISBLANK(TextVar), 0, VALUE(TextVar))でラップしてください。 - Number → Text:
TEXT(NumberVar)。千の位区切りなし、ローカライゼーションなし。 - Date → Text:
TEXT(DateVar)は ISOYYYY-MM-DDを返します。連結"Date: " & DateVarは実行中のユーザーのロケール形式を返します。複数ロケールの組織では驚くべきことです。 - Text → Date:
DATEVALUE(TextVar)はYYYY-MM-DDが必須です。 - Text → DateTime:
DATETIMEVALUE(TextVar)は GMT でのYYYY-MM-DD HH:MM:SSが必須です。 - Picklist → Text:
TEXT(PicklistField__c)は API 名を返し、ラベルを返しません。Custom Metadata 検索なしに、フォーミュラ内のラベルを比較することは不可能です。
一般的なパターン
パターン 1: BLANKVALUE デフォルトラップ
いつ使用するか: フォーミュラが null 値の可能性がある入力を消費する場合(必須でないレコードフィールド、オプションのスクリーン入力、空の可能性がある Get-Records の出力)。
仕組み:
// リスク:Discount__c が null の場合、NULL を返し、下流のすべてに伝播します。
{!recordVar.Amount} - {!recordVar.Discount__c}
// 安全:Discount__c が null の場合、デフォルト値 0 を設定します。
{!recordVar.Amount} - BLANKVALUE({!recordVar.Discount__c}, 0)
Text の場合:
BLANKVALUE({!recordVar.Description}, "(no description provided)")
欠落入力フォールバックが FALSE となるべき Boolean の場合:
IF(ISBLANK({!flagVar}), FALSE, {!flagVar})
代替案を使用しない理由: ラップをスキップして「入力を上流でチェック」することは、誰かが新しい呼び出し元を追加するまで機能します。フォーミュラ内の防御的なラップはコントラクトを明示的にし、上流リファクタリングに耐えます。
パターン 2: 高コストフォーミュラを Assignment にキャッシュ
いつ使用するか: Formula リソースが(a)単一フィールド参照より高コスト(連結、REGEX、ネストされた IF、5 以上のブランチを持つ CASE、日付計算)であり、(b)2 回以上参照される場合、特に Loop 本体内。
仕組み:
// Loop 本体内 — 前(6 × 200 = 1,200 回再評価):
// Decision 条件は {!isHighValueOpportunity} を使用
// Assignment 1 は {!isHighValueOpportunity} を使用して stageDescription を設定
// Assignment 2 は {!isHighValueOpportunity} を使用して nextAction を設定
// Assignment 3 は {!isHighValueOpportunity} を使用してメッセージをログ
// Update は {!isHighValueOpportunity} の場合、所有者を設定
// メールボディは {!isHighValueOpportunity} を参照
// 後(ループイテレーションごとに単一評価、合計 200 評価):
// Assignment "cacheHighValue": cachedHighValue (Boolean) = {!isHighValueOpportunity}
// 以降の 6 つの参照すべては {!cachedHighValue} を読み取ります(変数、フォーミュラではなく)
代替案を使用しない理由: フォーミュラを N 回参照することは、開発者が要素ごとのコストを精神的に追跡することに依存します。Assignment は単一評価コントラクトを明示的にし、監査が容易で、ループ本体編集に耐えます。
パターン 3: ピックリスト比較での ISPICKVAL
いつ使用するか: 単一選択ピックリストフィールド(PicklistField__c)または複数選択ピックリスト(MultiSelectField__c)を既知の値と比較します。
仕組み:
// 単一選択ピックリスト — 正しい:
ISPICKVAL({!recordVar.Stage__c}, "Closed Won")
// 単一選択ピックリスト — 誤り(ラベル対 API 名の比較は一部のロケールでサイレント失敗):
{!recordVar.Stage__c} = "Closed Won"
// 複数選択ピックリスト — 正しい:
INCLUDES({!recordVar.Industries__c}, "Healthcare")
// 否定:
NOT(ISPICKVAL({!recordVar.Stage__c}, "Closed Lost"))
// 複数の値:
OR(
ISPICKVAL({!recordVar.Stage__c}, "Closed Won"),
ISPICKVAL({!recordVar.Stage__c}, "Closed Lost")
)
// ピックリストを API 名 Text に変換して追加文字列操作:
TEXT({!recordVar.Stage__c}) & " — recorded"
代替案を使用しない理由: = は英語のみの組織でアクティブなデフォルト値に対して機能し、翻訳パックが有効になるか誰かが API 名を変更せずにピックリストラベルを変更するとすぐに破損します。ISPICKVAL は API 名と比較し、ロケールに依存しません。
決定ガイダンス
| シナリオ | 推奨される方法 | 理由 |
|---|---|---|
| 1 回実行される計算、ループ外 | Formula リソース | 遅延評価コストは 1。可読性は変数のインダイレクションに勝ります。 |
| ループ本体内で 3 回以上参照される計算 | 型付き変数への Assignment(キャッシュ) | 参照ごとの再評価を回避します。 |
| Boolean/Number/Date の値での分岐 | Decision 条件(フォーミュラまたは演算子) | Decision は明示的な分岐プリミティブです。Decision 条件内のフォーミュラは問題無し。 |
| 単一選択ピックリストでの分岐 | ISPICKVAL(...) を使用する Decision 条件 | ロケール安全。リテラルに対する = は既知の P1 バグです。 |
| 多くのフィールドにわたる複数ステップの変換(Apex のようなロジック) | 型付き出力を返す Apex Invocable Action | 3 層超のネストされたフォーミュラは、Apex の方がテスト可能、デバッグ可能、再利用可能です。 |
| 式が 4,500 文字以上で増加中 | 複数の構成 Formula リソース、または Apex Invocable Action | 単一フォーミュラの上限は 5,000 文字です。構成により各レイヤーを上限以下に保ちます。 |
| 複数ロケール組織で実行される Date を含む文字列連結 | TEXT(DateVar) + 手動フォーマット、または Invocable | 暗黙的な連結は実行中ユーザーロケールを使用。明示的な TEXT() は決定論的な ISO を返します。 |
| null 入力を持つ 5 以上の AND/OR からなる Boolean 式 | 最初に BLANKVALUE で各入力をラップしてから組み合わせ | NULL 伝播により、単純な AND(...) は FALSE ではなく NULL に評価されます。 |
| 複数選択に対する「これらのピックリスト値のいずれかを持っているか」 | INCLUDES(MultiPicklistField__c, "value") | INCLUDES は複数選択の唯一の正しいプリミティブです。 |
推奨される作業フロー
- コントラクトを述べます。 フォーミュラの期待される戻り値型、すべての入力、どの入力が null 値の可能性があるか、フォーミュラがどこで参照されるかを記述してください。参照に Loop 本体が含まれる場合は、期待されるイテレーション数を記述してください。
- コンテキストを選択します。 Formula リソース(再利用可能)、Decision 条件(1 回限りの分岐)、スクリーンコンポーネントプロパティ(描画ごと)。同じ式が 3 つ以上の場所で必要な場合は、Formula リソースを選択して呼び出しサイトを DRY にしてください。
- NULL ガードを最初に作成します。 NULL ガード式の残りを構成する前に、null 値の可能性のある入力を
BLANKVALUEまたはIF(ISBLANK(...), default, value)でラップしてください。NULL ガードは上流で追加した方が、実行時に null が下流要素に現れた後に改造するよりもはるかに安価です。 - すべての型の強制変換を明示的にします。 暗黙的な強制変換を
VALUE()、TEXT()、DATEVALUE()、DATETIMEVALUE()に置き換えます。ピックリストについては、=をISPICKVAL()に置き換え、「含む」をINCLUDES()に置き換えます。 - 再評価コストを監査します。 Formula リソースを参照する場所の数を数えます。いずれかが Loop 内にある場合は、
参照数 × ループ_イテレーション数をカウントします。積が約 500 を超え、フォーミュラが非自明の場合は、Assignment キャッシュ変数にリファクタリングしてください。 - 5,000 文字の上限をチェックします。 フォーミュラが約 4,500 文字を超える場合は、デプロイ時にハード制限に当たる前に、複数の構成 Formula リソースに分割してください。
- Flow Debug で検証します。 現実的な null とエッジ入力(空の Get-Records 結果、欠落オプションフィールド、0 個の選択を持つピックリスト、複数タイムゾーン日付境界)でフローを実行してください。下流の Decision または Update に NULL が表示されないことを確認してください。
確認チェックリスト
- null 値の可能性があるすべての入力は
BLANKVALUEまたはIF(ISBLANK(...), default, value)でラップされています。 - すべてのピックリスト比較は
ISPICKVAL(単一)またはINCLUDES(複数)を使用し、=ではありません。 - すべての Text ↔ Number、Text ↔ Date、Text ↔ DateTime 強制変換は
VALUE/TEXT/DATEVALUE/DATETIMEVALUEを明示的に使用します。 - Loop 本体内で参照される Formula リソースは、自明(単一フィールド参照、単一算術演算)であるか、Assignment にキャッシュされています。
- 各 Formula リソースは 4,500 文字以下です。より大きな式には構成チェーンが使用されます。
-
TODAY()対NOW()の選択は、複数タイムゾーン組織で使用される場合に文書化されています。正しいタイムゾーンセマンティクスが望まれることを確認します。 - Decision 条件フォーミュラとスクリーンコンポーネントフォーミュラは、スタンドアロン Formula リソースと同じ NULL セーフティ + 強制変換チェックリストで確認されています。
- 構成フォーミュラチェーンは 3 レイヤーのネスト(FormulaA → FormulaB → FormulaC)を超えません。より深いチェーンは Apex Invocable になるべきです。
- Workflow のみまたは Validation Rule のみのフォーミュラ関数は使用されていません(例:
PRIORVALUE、ISCHANGED)。
Salesforce 固有の落とし穴
- NULL は算術演算子と Boolean 演算子を通じて伝播します。
NULL + 1 = NULL、NULL && TRUE = NULL、NULL > 0 = NULL。NULL で汚染されたフォーミュラに依存する Decision 条件は、「Comparison value cannot be null」をスロー、またはデフォルト結果を静かに取得します。どちらも P1 本番環境障害です。 - ピックリストリテラルに対する
=はサイレントロケールバグです。{!Account.Industry} = "Healthcare"はユーザーが見る値(ラベル)をリテラル文字列と比較します。翻訳パック、ラベル編集、またはAPI 名対ラベルのドリフトはすべて、警告なしに比較を FALSE に変えます。常にISPICKVALを使用してください。 - Loop 本体内の遅延再評価は CPU コストを乗算します。
{!FormulaResourceName}への参照は常にフォーミュラを再実行します。6 回の参照 × 200 イテレーション = 1,200 評価。フォーミュラが非自明で、ループ本体内で 2 回以上参照される場合は、Assignment でキャッシュしてください。 - フォーミュラごと 5,000 文字の上限。 プラットフォームは 5,000 文字以上のフォーミュラを含むデプロイを拒否します。複数の Formula リソースを構成します(FormulaA は FormulaB を参照し、FormulaB は FormulaC を参照)。各子は独立してカウントされます。3 層超の構成は臭いです。Apex Invocable に昇格させてください。
TODAY()とNOW()は異なるタイムゾーンを使用します。TODAY()は実行中のユーザーのローカルタイムゾーン内のローカル日付を返します。NOW()は組織のデフォルトタイムゾーンを返します。複数タイムゾーン組織では、「太平洋時間午前 1 時の TODAY」と「東部時間午前 1 時の TODAY」は異なる日付です。TODAY()と格納された Date を比較するフォーミュラは、組織のデフォルトタイムゾーンの反対側のユーザーに対して 1 日ずれる可能性があります。TEXT(picklist)は API 名を返し、ラベルではありません。TEXT(PicklistField__c)から作成されたユーザー向けの文字列は API 名を表示します。Custom Metadata マッピング上で Get-Records、またはラベルにマップするハードコードされたCASEを使用してください。- 暗黙的な Date から Text への連結は実行中ユーザーロケールを使用します。
"Created on " & {!recordVar.CreatedDate}は米国ユーザーに「10/27/2026」を返し、UK ユーザーに「27/10/2026」を返します。決定論的な ISO 出力のためにTEXT({!recordVar.CreatedDate})を使用するか、TEXT()のLEFT/MID/RIGHTで明示的にフォーマットしてください。 - Decision 条件は Decision が実行されるたびに参照されたフォーミュラを評価します。 Loop 本体内の Decision が 3 つの Formula リソースを参照する場合、イテレーションごとに 3 評価のコストがかかります。要素レベルの参照と同じ方法で監査できます。
出力成果物
| 成果物 | 説明 |
|---|---|
| Formula リソース定義 | フロントマター(名前、戻り値型、入力変数への依存関係)、NULL ガードと明示的な強制変換を含む式本体。 |
| キャッシュ変数 Assignment リファクタリング | フォーミュラが Loop 内で再評価されていた場合。結果を具体化する Assignment と、キャッシュ変数を読み取るように切り替えられた下流参照のリスト。 |
| 構成フォーミュラチェーン | 元の式が 4,500 文字を超えた場合。親と子の Formula リソース、各々が独立して制限以下。 |
| Decision 条件の書き直し | 元の = ピックリストラベル比較がバグだった場合。ISPICKVAL または INCLUDES の書き直し。 |
| 確認レポート | 追加された NULL ガード、書き直されたピックリスト比較、明示的にされた型強制、修正された再評価ホットスポットの項目化リスト。Flow XML 内のライン レベル ポインターまたはスクリーンショット証拠付き。 |
関連スキル
flow-resource-patterns— リソース命名、スコープ、Variable / Constant / Formula / Choice / Stage リソース間での選択に関する広いガイダンス。このスキルはフォーミュラ固有のサブセットを処理します。flow-decision-element-patterns— フォーミュラが Decision 結果の条件として作成される場合、これら 2 つのスキルをペアにしてください。admin/formula-fields— オブジェクト上のレコードレベルのフォーミュラフィールド(異なる
ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- PranavNagrecha
- ライセンス
- Apache-2.0
- 最終更新
- 2026/5/9
Source: https://github.com/PranavNagrecha/AwesomeSalesforceSkills / ライセンス: Apache-2.0