条件と答えを表にまとめる(Decision Table)
Flow Designer で「変更のリスクが高ければ CAB 承認、低ければマネージャー承認」のような分岐を組むとき、条件分岐を if/else で延々と書くのは見通しが悪くなります。ServiceNow にはこの「条件 → 答え」の対応表を1か所にまとめる Decision Table(決定表)があります。その実体がどのテーブルに保存され、入力と行(条件)と答えがどうつながるのかを、PDI(Zurich)の OOTB で確認した実測値とともに示します。
この記事は、ServiceNow の Incident や Change は触ったことがある開発者・管理者を対象にしています。想定しているのは、Flow Designer も少し使うが、Decision Table の裏側のテーブル構造をこれから把握したい人です。決定表を構成する sys_decision / sys_decision_question / sys_decision_input の役割を確認します。あわせて、OOTB の「Normal Change Policy」を例に、入力と行の読み方も確認します。
この記事で分かること
決定表の実体が sys_decision(Decision Table)に保存され、1行が sys_decision_question、入力が sys_decision_input に分かれること
sys_decision が answer_table と answer_type で「どのテーブルのレコードを答えに返すか」を持つこと
1行(sys_decision_question)が condition(条件)と answer(答え)の組であること
OOTB の Normal Change Policy が入力2・行3でできていること
決定表が Flow のアクション(Apply Change Approval Policy など)から引かれること
実機で確認するときに見るべき一覧URL
この記事の内容は ServiceNow の PDI(Personal Developer Instance)Zurich(ズーリッヒ)リリースの OOTB(初期設定)で確認した個人メモです。本番環境やカスタマイズ済み環境では、決定表の数・入力・行・answer_table の内容が異なる場合があります。
Decision Table(決定表)とは
決定表は、いくつかの入力値を条件に照らして、対応する「答え」のレコードを返す対応表です。表計算でいう「IF の入れ子」を、行(条件)と列(入力)のマトリクスとして1か所にまとめたものと考えると分かりやすいです。
この対応表の本体が sys_decision(画面上のラベルは Decision Table)です。今回確認した PDI(Zurich / OOTB)では、sys_decision に 24 件 の決定表があり、変更承認・認証ポリシー・デプロイ関連など機能ごとの表が登録されていました。

決定表は3つのテーブルで表されます。表のヘッダが sys_decision、表の1行が sys_decision_question、表の入力(列)が sys_decision_input です。この分かれ方を押さえると、決定表の中身がそのまま読めるようになります。
決定表を構成する3テーブル
決定表は1テーブルではなく、ヘッダ・行・入力の3つに分かれて保存されます。今回確認した PDI(Zurich / OOTB)では、それぞれ次の構造でした。

ヘッダ・行・入力の役割
sys_decision(Decision Table) … sys_metadata を継承し、固有フィールドは15です。決定表のヘッダで、answer_table(答えを格納するテーブル)を持ちます。
sys_decision_question(Decision) … sys_metadata を継承し、固有フィールドは13です。表の1行にあたり、condition(条件)と answer(答え)の組を持ちます。decision_table で親の sys_decision を指します。
sys_decision_input(Decision Input) … var_dictionary を継承し、固有フィールドは3、件数は32です。決定表の入力(評価する変数)を定義します。model で親の sys_decision を指します。
なぜ入力は var_dictionary を継承するのか
入力が var_dictionary を継承しているのは、入力が「変数の定義」だからです。決定表に渡す change_request や真偽値のような入力を、ServiceNow の変数(var_dictionary)の仕組みで型付きで定義します。だから sys_decision_input の固有フィールドは model と parent くらいで、型や参照先は var_dictionary 側から受け取ります。

テーブル定義は、次のURLで確認できます(先頭の <YourInstance> は自分のインスタンスに置き換えてください)。
https://<YourInstance>/sys_db_object_list.do?sysparm_query=name=sys_decision^ORname=sys_decision_question^ORname=sys_decision_input
sys_decision の主なフィールド(答えをどこに返すか)
決定表のヘッダ sys_decision は、「この表が何を返すか」を決めるフィールドを持ちます。役割で見ると、表示名・有効状態・答え先・公開状態に分かれます。
主なフィールドの代表は次のとおりです。ラベルは画面上の表示名で記載します。
主なフィールド一覧
answer_table(Answer table) … 答え(結果)を格納するテーブル名。Normal Change Policy なら chg_approval_def(変更承認定義)です。
answer_type(Answer type) … 答えの返し方。今回の PDI で有効な選択肢は reference のみでした。つまり答えは answer_table の1レコードへの参照として返ります。
status(Status) … 公開状態。draft / published の2値です。
active(Active) … 有効/無効。status とは別フラグです。

sys_decision の固有フィールドは、次のURLで一覧できます。
https://<YourInstance>/sys_dictionary_list.do?sysparm_query=name=sys_decision^elementISNOTEMPTY
answer_table は機能ごとに異なる
決定表が指す answer_table は機能ごとに違います。今回の PDI では、変更承認系が chg_approval_def を指していました。認証ポリシー系は sys_auth_policy_answer、複数の値を返す表は sys_decision_multi_result です。

公式の Decision Tables(Zurich) に、決定表が条件と入力から結果を導く仕組みの概要が説明されています。
1行=条件と答え(sys_decision_question)
決定表の1行は sys_decision_question に保存されます。画面ラベルは Decision です。1行が「この条件のとき、この答えを返す」という1分岐にあたります。

1行が持つ主なフィールドは次のとおりです。
sys_decision_question の主なフィールド
condition(Condition) … この行が成立する条件。change_request の state やリスクなどを組み合わせたエンコードクエリで保持されます。
answer(Answer) … 条件成立時に返す答え。document_id(多態参照)で、answer_table のレコードを指します。
default_answer(Default answer) … どの行の条件にも当たらないときに使う既定行かどうかの真偽値。
order(Order) … 評価順。
今回の PDI(Zurich / OOTB)では、sys_decision_question は 21 件 あり、行を持つ決定表は24中14でした。残りは入力だけ定義されて行が未登録の表です。
実機で読む:Normal Change Policy(入力2・行3)
OOTB の「Normal Change Policy」を開くと、決定表が入力と行でできていることがそのまま見えます。これは通常変更(Normal Change)の承認ポリシーを判定する決定表で、answer_table は chg_approval_def(変更承認定義)です。
まず入力(sys_decision_input)は 2 件 でした。change_request(参照型)と manager_approved(真偽値)です。「どの変更要求か」と「マネージャー承認済みか」を入力として受け取ります。

このフォームには Execution に 「Run all decisions that match」 という設定も見えました。条件に当たった行をすべて実行する動作で、決定表の評価が「最初の1行だけ」とは限らないことを示しています(評価の実行時挙動そのものは検証範囲外です)。
次に行(sys_decision_question)は 3 件 でした。各行が condition と answer を持ち、変更のリスクや状態に応じて異なる承認定義を返します。

3行の condition と答えは次のようになっていました。
Requires CAB Approval … 条件は change_request.state=-3(Assess)かつ risk が 2,3(高め)。エンコードクエリは change_request.state=-3^change_request.riskIN2,3^OR...(以降さらに条件が続く複合クエリ)でした。答えは CAB Approval の承認定義。
Low Risk Manager approval … 条件は change_request.state=-3 かつ risk=4(Low)かつ manager_approved=false。エンコードクエリは change_request.state=-3^change_request.risk=4^manager_approved=false でした。答えはマネージャー承認の定義。
Assess Technical Approvals … 条件は change_request.state=-4(New)。エンコードクエリは change_request.state=-4 のみでした。答えは技術承認の定義。
入力 change_request と manager_approved の値が、これら3行の condition に照らされ、成立した行の answer(chg_approval_def のレコード)が返ります。これが決定表の読み方です。
入力と行は、次のURLで一覧できます。
入力(列) : https://<YourInstance>/sys_decision_input_list.do?sysparm_query=model=<決定表のsys_id>
行(条件) : https://<YourInstance>/sys_decision_question_list.do?sysparm_query=decision_table=<決定表のsys_id>^ORDERBYorder公式の Create a decision table(Zurich) に、入力(inputs)と決定行(decisions)を定義して結果を返す決定表の作り方が説明されています。
status と評価のされ方
決定表には status(draft / published)があり、公開された表が評価で使われます。active(boolean)とは別のフラグで、編集中は draft、使える状態にしたら published になります。

公開された決定表は、Flow Designer のアクションやサブフロー、スクリプトから引かれます。今回の PDI では、変更承認を決定表で判定する Flow アクション 「Apply Change Approval Policy」 が存在し、Normal Change Policy / Emergency Change Policy のような chg_approval_def を返す表と組み合わさる構成でした。

入力値を各行の condition に照合し、成立した行の answer を返します。どの行にも当たらなければ default_answer の行が使われます。注意したいのは、ここで確認したのはテーブル構造と OOTB のデータであり、評価エンジンの内部挙動(どの順で照合し、複数一致をどう扱うか)は検証範囲外という点です。Normal Change Policy のフォームに見えた Execution=「Run all decisions that match」のように、表ごとの実行設定で挙動は変わります。
公式の Use decision tables(Zurich) に、Flow やサブフローから決定表を呼び出して結果を使う流れが説明されています。
ServiceNow における Decision Table のポイント
決定表の実体は sys_decision(Decision Table) に保存されます。今回の PDI では 24 件でした。
決定表は3テーブルに分かれます。ヘッダ sys_decision(固有15)、行 sys_decision_question(固有13)、入力 sys_decision_input(固有3・var_dictionary 継承)。
sys_decision は answer_table と answer_type で「どのテーブルのレコードを答えに返すか」を持ちます。今回の PDI で answer_type の有効値は reference のみでした。
1行(sys_decision_question)は condition と answer の組 で、document_id で answer_table のレコードを指します。
OOTB の Normal Change Policy は 入力2(change_request / manager_approved)・行3 で、chg_approval_def の承認定義を返していました。
決定表は Flow アクション(Apply Change Approval Policy など)から引かれ、published のものが評価に使われます。
決定表を調べるときは、まず「この表は何(answer_table)を返すか」を見て、入力(sys_decision_input)と行(sys_decision_question の condition / answer)をセットで読むと、分岐ロジックがそのまま読み取れます。
実機で気づいたこと
Normal Change Policy は入力2・行3とシンプルでしたが、24件全体で見ると規模はかなりばらついていました。入力が最も多い決定表は Deployment Migration to ReleaseOps の9件でした。次いで Enforce MFA for non-SSO logins が4件、Callback Topic Policy が3件と続きます。行を持つ14件の内訳では、Deployment Environment Type Flow の5行が最多で、Normal Change Policy の3行がそれに続きました。24件全体では sys_decision_question(行)が21件、sys_decision_input(入力)が32件で、単純計算では1表あたり入力1.3個・行0.9個ほどになります。Normal Change Policy だけを見ていると「決定表=数行の分岐」という印象になりますが、実際には10近い入力を持つ表もありました。単純な if/else の置き換えというより、もう少し大きな判定ロジックの置き場としても使われているようです。
sys_decision テーブル自体をカスタマイズしている周辺スクリプトは、Business Rule・Client Script・UI Policy・UI Action の4種類でした。collection または table に sys_decision を指定した検索でそれぞれ1件ずつ見つかり、思ったより少ない数でした。一方で Access Control はテーブルレベル10・フィールドレベル8と手厚く、決定表そのものの編集はスクリプトよりACLで制御する設計に見えます。複数の答えを返す sys_decision_multi_result は5件登録されていて、その明細行にあたる sys_decision_multi_result_element も同じく5件でした。一方で差分を扱う sys_decision_delta は0件でした。用途としては用意されているものの、今回のOOTBでは使われていないテーブルということになります。
次に読むなら
次に確認するなら、決定表を実際に引く側です。Flow Designer の Decision アクションやサブフローからの呼び出し、スクリプトでの DecisionTableAPI、そして answer_table 側(chg_approval_def や sys_auth_policy_answer)のレコードがどう使われるかです。「条件をまとめる決定表」と「それを引くフロー」を分けて理解すると、承認や認証の分岐がどこで決まっているかを追いやすくなります。これらは本記事の「status と評価のされ方」で確認した仕組みの先にあたり、別記事で扱う予定です。
承認の仕組みそのものについては、別記事の承認エンジン(sysapproval_approver)で、承認レコードの生成元・State・グループ承認を整理しています。決定表が返す chg_approval_def は、この承認の手前で「どの承認が要るか」を決める側にあたります。
用語
PDI:Personal Developer Instance(無料の検証用インスタンス)
OOTB:Out of the Box(初期設定のまま)
Decision Table(sys_decision):決定表そのもの(ヘッダ・答え先を持つ)
Decision(sys_decision_question):決定表の1行(条件→答え)
Decision Input(sys_decision_input):決定表の入力(評価する変数・列)
answer_table:答え(結果)を格納するテーブル
自分の環境で確認するときのURL
自分のインスタンス名を先頭に付けると、同じ画面が開けます。
テーブル定義 : sys_db_object_list.do?sysparm_query=name=sys_decision^ORname=sys_decision_question^ORname=sys_decision_input
決定表一覧 : sys_decision_list.do?sysparm_query=active=true^ORDERBYlabel
行(条件)一覧 : sys_decision_question_list.do?sysparm_query=ORDERBYdecision_table
入力一覧 : sys_decision_input_list.do
フィールド一覧 : sys_dictionary_list.do?sysparm_query=name=sys_decision^elementISNOTEMPTY検証範囲とおことわり
確認した範囲:sys_decision / sys_decision_question / sys_decision_input の階層、固有フィールド数(15 / 13 / 3)、主要フィールドと参照先、status と answer_type の choice、レコード件数、answer_table の用途別分布、Normal Change Policy の入力2・行3の構造、周辺設定件数。すべて PDI(Zurich / 2026-06-22 確認)の OOTB で取得しています。
確認していない範囲(断定しません):決定表の評価エンジンの実行時挙動(照合順・複数一致の扱い・DecisionTableAPI / スクリプト連携)、Decision Builder UI の操作詳細、各 condition の評価結果、answer_type の reference 以外の値、Excel 連携、本番でのカスタム決定表。これらは別記事で扱います。
今回は PDI の OOTB(初期設定)を前提に、決定表の構造を確認しました。実環境では、既存のカスタマイズによって決定表の数・入力・行・answer_table が変わります。