メインコンテンツへスキップ
見出し画像

選択肢Choice(sys_choice):ドロップダウンの中身


    Incident の State を開くと「New / In Progress / On Hold / Resolved / Closed / Canceled」が並びます。Contact type なら「Email / Phone / Self-service」、Category なら「Inquiry / Software / Hardware …」。こうしたドロップダウンの選択肢は、いったいどこに書かれているのでしょうか。

    答えは sys_choice という1つのテーブルです。 State も Category も Contact type も、画面に出てくる「選べる候補」は、すべて sys_choice に「1選択肢=1レコード」で並んでいます。前回の記事で見た sys_dictionary(フィールド定義)が「このフィールドはドロップダウンだ」と宣言し、その候補の中身を sys_choice が持つ、という二段構えです。

    この記事は、Incident は触ったことがあり、フォームのドロップダウンが「なぜその値なのか」「どこを直せば候補を足せるのか」を自分で調べられるようになりたい管理者・開発者を想定しています。扱うのは、選択肢の正体(value と label)、並び順(sequence)、無効化(inactive)の3点です。さらに「別のフィールドで候補が変わる」依存選択肢(dependent_value)も、実機の sys_choice で確認します。最後に、State へ選択肢を1つ足すなら sys_choice をどう編集するか、という設定例も付けます。

    この記事で分かること

    • ドロップダウンの選択肢は sys_choice に「1選択肢=1レコード」で記録されていること

    • 1レコードが name(テーブル)・element(フィールド)・value(保存値)・label(表示名)で成り立つこと

    • 並び順は sequence、候補から外すのは inactive で制御されること

    • 選択肢が「別のフィールドの値」で切り替わる依存選択肢(dependent_value)の仕組み

    • 選択肢は親テーブル(task)に定義され、子テーブル(incident)が継承する場合があること

    • ServiceNow の選択肢を、自分のインスタンスで調べる URL

    この記事の内容は ServiceNow の PDI(Personal Developer Instance)Zurich(ズーリッヒ)リリースの OOTB(初期設定)で確認した個人メモです。本番環境やカスタマイズ済み環境では、選択肢の件数・ラベル・依存関係などが異なる場合があります。


    選択肢(Choice)と sys_choice とは

    フォームのドロップダウンに出てくる候補は、System Definition > Choice Lists(sys_choice)に1件ずつ入っています。 Choice list は ServiceNow の「フィールド型」のひとつです。公式ドキュメントでも Choice list field type として、固定の選択肢から1つ(または複数)を選ばせる型だと説明されています。

    ServiceNow では、選択肢そのものも「レコード」として管理されています。State に「Pending」を足したければ、sys_choice に1レコード増やす。ある候補を見せたくなければ、そのレコードの inactive を true にする。フォームの裏では、選択肢の出し入れがレコードの追加・無効化として行われています。

    今回確認した PDI(Zurich / OOTB)では、sys_choice の総件数は 18,662 件でした。ただしこれは全言語・全テーブルの合計で、英語(language=en)だけに絞ると 14,863 件です。同じ選択肢が言語ごとに別レコードとして複製されるため、日本語環境を足すと件数が増えていく構造です。


    画像

    この記事では、まず1レコードの構造(value / label など)を見て、次に並び順と無効化、最後に「別フィールドで候補が変わる」依存選択肢へと進みます。


    選択肢1レコードの構造(name / element / value / label)

    sys_choice の1レコードは「どこの・何という値・どう見せるか」で決まります。 どこの選択肢かは name(テーブル)と element(フィールド)の2つ、何という値かは value(DBに保存される内部値)、どう見せるかは label(ユーザーに見える表示名)です。


    画像

    たとえば incident の State で「In Progress」を選ぶと、DB に保存されるのは label の "In Progress" ではなく value の "2" です。 sys_choice のこのレコードは name=incident / element=state / value=2 / label=In Progress という4点セットです。連載で「State は内部的に数字で持つ」と書いたのは、この value と label の分離が理由です。

    sys_choice の固有フィールドは18あり、主なものは次のとおりです。


    画像

    フィールドを4つのグループで読む

    各フィールドの役割を整理すると、選択肢は「場所」「中身」「見せ方」「連動」の4グループで読めます。


    画像
    • 場所(name / element) … どのテーブルの、どのフィールドの選択肢か。State の候補を探すなら name=incident^element=state で絞ります。

    • 中身(value / label) … value は保存される内部値、label は表示名。value は変えると既存データと食い違うので慎重に、label は表示だけなので比較的安全に直せます。

    • 見せ方(sequence / inactive) … sequence は並び順、inactive は候補から外すフラグ。

    • 連動(dependent_value / language) … dependent_value は別フィールドの値に連動する依存選択肢(後述)、language はラベルの言語です。

    特定フィールドの選択肢は、次の URL で一覧できます(例は incident の state。<YourInstance> は自分のインスタンスに置き換えてください)。

    https://<YourInstance>/sys_choice_list.do?sysparm_query=name=incident^element=state^inactive=false^ORDERBYsequence


    画像

    incident.state の選択肢を実例で見る

    今回確認した PDI(Zurich / OOTB)の incident.state は、次の6件でした。


    画像

    value が 4・5 を飛ばして 6・7・8 になっているのは、過去に使われていた値が無効化(inactive)されて欠番になっているためです。value は連番である必要はなく、いったん使った値は欠番にしても既存データとの整合のために残されます。


    辞書(sys_dictionary)との結びつき

    選択肢の中身は sys_choice にありますが、「このフィールドはそもそもドロップダウンなのか」を決めているのは前回見た sys_dictionary(フィールド定義)です。辞書側の choice 列が「ドロップダウンにする」と宣言し、その候補を sys_choice が name+element で供給する。これが両者の結びつきです。


    画像

    incident.state の choice 値を見る

    今回確認した PDI(Zurich / OOTB)では、incident(実体は親 task)の state の辞書レコードは choice=3 でした。この値は「Dropdown without -- None --(既定値必須)」を指します。choice 列の主な値は次のとおりです。

    • 0 … None。選択肢なしで、ただの文字列として扱われます。

    • 1 … Dropdown with -- None --。候補を表示し、空欄も選べます。

    • 3 … Dropdown without -- None --。空欄を選ばせない設定で、既定値が必須になります。

    つまり、選択肢を1つも作っていなくても辞書の choice を 1 や 3 にすればドロップダウンになり、候補がなければ空のドロップダウンになります。逆に sys_choice にレコードがあっても辞書の choice が 0 なら、フォームはドロップダウンになりません。両方を見て初めて挙動が読めます。辞書側の入力画面は公式の Choice list field type でも説明されています。

    辞書レコードは次の URL で確認できます。

    https://<YourInstance>/sys_dictionary_list.do?sysparm_query=name=incident^element=state

    並び順(sequence)と無効化(inactive)

    ドロップダウンの並び順は sequence、候補から消すのは inactive です。 どちらも選択肢を「削除せずに」コントロールする仕組みで、運用ではこの2つを触ることが多くなります。

    Contact type で見る sequence と inactive

    Contact type を例に見てみます。次の URL で、incident.contact_type の選択肢を sequence 順に一覧できます。

    https://<YourInstance>/sys_choice_list.do?sysparm_query=name=incident^element=contact_type^ORDERBYsequence


    画像

    今回確認した PDI(Zurich / OOTB)の incident.contact_type は、有効な候補が6件でした。中身は Chat / Email / Phone / Self-service / Virtual Agent / Walk-in です。これに無効化済みの1件を加えて全7件になります。inactive=true の選択肢はドロップダウンの候補には出ませんが、過去にその値で保存されたレコードの表示は壊れません。だから「もう使わない候補」は削除ではなく inactive にするのが定石です。

    • sequence … 数字が小さいほど上に出ます。State は New=1 → Canceled=8 のように、業務の流れに沿った順番が付いています。

    • inactive … true にすると候補から外れます。既存データの value は保持されるので、後方互換のために「無効化して残す」運用になります。

    候補数はフィールドごとに違いました。今回の incident では State=6 件、Category=6 件、Contact type=7 件です。さらに Close code=10 件、Hold reason=4 件、Notify=3 件でした(いずれも language=en)。incident の選択肢は全言語合計で 64 件です。

    いま新しく State に候補を1つ足すなら

    ここまでの構造をそのまま設定に移します。incident の State に「Pending(保留待ち)」を1つ足すとします。System Definition > Choice Lists(sys_choice)で New レコードを作り、次の値を入れます。

    • Table … Incident [incident](name に相当)。

    • Element … state(どのフィールドの候補か)。

    • Value … 既存と重複しない内部値。State は数字運用なので数値を選びます。今回の OOTB は 1〜3・6〜8 を使っているため、欠番か十分大きい値(例 100)を取ります。

    • Label … Pending(フォームに表示される文字)。

    • Sequence … 3.5 のように、On Hold(3)と Resolved(6)の間に出したい位置の数字。

    • Inactive … オフのまま(候補に出す)。

    保存すると incident フォームの State ドロップダウンに Pending が並びます。やめるときは削除ではなく Inactive をオンにすれば、過去に value=100 で保存したレコードを壊さずに候補から外せます。State はワークフローやビジネスルールが value を直接参照することが多いフィールドです。追加した value を後続処理が無視しないかは、足したあとに動作を確認してください(参照側の網羅は検証範囲外)。


    別フィールドで候補が変わる:依存選択肢(dependent_value)

    選択肢の候補は固定とは限りません。dependent_value を使うと、「別のフィールドで選んだ値」によって、このフィールドのドロップダウン候補が切り替わります。 Category を Hardware にすると Subcategory の候補が変わる、という挙動はこれで実現されています。

    実機の PDI(Zurich / OOTB)では、dependent_value が入っている選択肢は 565 件ありました。分かりやすい OOTB の例として、ハードウェア CI の cmdb_ci_hardware.hardware_substatus(詳細ステータス)を見ます。これは親フィールド install_status(Hardware status)の値によって、選べる substatus が切り替わる構造です。


    画像
    https://<YourInstance>/sys_choice_list.do?sysparm_query=name=cmdb_ci_hardware^element=hardware_substatus^inactive=false^ORDERBYdependent_value^ORDERBYsequence


    画像

    親の値ごとに、表示される子の候補は次のように分かれていました。


    画像

    ポイントは、dependent_value に入れるのは親フィールドの value(内部値)であって、label(表示名)ではないことです。たとえば「retired のとき Sold を出す」なら、Sold の選択肢レコードの dependent_value に親の内部値 "retired" を入れます。表示名で書くと一致せず、候補が出なくなります。

    依存選択肢の仕組み自体は、辞書側で「どのフィールドに依存するか」を指定し、選択肢側で dependent_value に親の値を入れる、という二段階で成り立ちます。OOTB では他にも同じ仕掛けが動いていました。ast_contract.substate は契約の state に依存します。cmdb_categories.subcategory は category に依存します。


    選択肢は継承される:impact / urgency と priority

    最後に、連載の継承の話とつながる観察です。incident の Impact と Urgency は明らかにドロップダウンですが、name=incident で sys_choice を探しても 0 件でした。候補が出ているのに選択肢レコードが無い、という状態をどう読むかが、この節の論点です。

    答えは継承です。impact / urgency の選択肢は親テーブル task 側に定義されており、子の incident はそれを引き継いで表示しています。 実際に name=task で探すと、impact も urgency も「1 - High / 2 - Medium / 3 - Low」の3件ずつが見つかりました。テーブルの継承(super_class)は、選択肢にも効いています。


    画像

    もう一段あります。同じフォームにある Priority は、sys_choice を name=incident でも name=task でも探しても1件もありませんでした。priority は選択肢を持たず、impact と urgency の組み合わせから算出される計算フィールドだからです。冒頭のフォーム画面で Priority だけドロップダウンではなく「1 - Critical」と灰色で表示されていたのは、これが理由でした。

    • 選択肢を持つフィールド … state / category / contact_type など。sys_choice に候補が並ぶ。

    • 親から継承するフィールド … impact / urgency。選択肢は親 task 側にある。

    • 選択肢を持たない計算フィールド … priority。impact × urgency で決まる。

    「ドロップダウンに候補が出ているのに sys_choice に無い」ときは、親テーブルを継承していないか、そもそも計算で決まる値ではないかを疑うのが、今回の PDI で得た読み方です。


    ServiceNow の選択肢を読むときのポイント

    • ドロップダウンの候補は sys_choice に「1選択肢=1レコード」で入っています。今回の PDI では総数 18,662 件(en だけで 14,863 件)でした。

    • 1レコードは name(テーブル)・element(フィールド)・value(保存値)・label(表示名)で「どこの・何という値・どう見せるか」が決まります。DBに入るのは value で、label は表示専用です。

    • 並び順は sequence、候補から外すのは inactive です。使わない候補は削除せず inactive にして、既存データとの整合を保ちます。

    • 「このフィールドがドロップダウンか」は辞書(sys_dictionary)の choice 列が決め、候補の中身を sys_choice が供給します。今回の incident.state は choice=3(既定値必須のドロップダウン)でした。

    • dependent_value を使うと、別フィールドの value に応じて候補が切り替わります(Category→Subcategory など)。今回の PDI では依存選択肢が 565 件あり、cmdb_ci_hardware.hardware_substatus が分かりやすい例でした。

    • 選択肢は親テーブルから継承されることがあります。impact / urgency は task 側に定義され incident が引き継ぎ、priority はそもそも計算フィールドで選択肢を持ちませんでした。

    選択肢を調べたくなったら、まずフォームのドロップダウンの内部名(element)を確認し、sys_choice を name=テーブル名^element=フィールド名 で絞ります。候補が出てこなければ、親テーブルを継承していないか・計算フィールドではないかを疑います。この手順で、連載で見てきた State や Category の正体を自分で再現できます。


    実機を見て気づいたこと

    • sys_choice を language 別に数えると、en が 14,863件に対し fr・ja・de・es はそれぞれ165〜169件でした。同じ選択肢が言語ごとに複製される構造だと分かっていても、非英語言語がここまで少ないのは意外でした。翻訳パックを入れても全選択肢を追いかけて複製しているわけではなく、一部だけがローカライズされている状態のようです。

    • inactive=true の選択肢は全体で4,352件、総数18,662件の約23%を占めていました。「使わなくなった候補は削除せず無効化する」という運用は本文でも触れましたが、実際に4分の1近くが無効化されたまま残っているのを見ると、この方針がかなり徹底されていることが数字からも伝わってきます。


    次に読むなら

    この記事は、前回の sys_db_object / sys_dictionary(テーブルとフィールドの定義)の続きにあたります。辞書の internal_type が choice のとき、その候補がどこにあるか。その答えが sys_choice でした。次は、選択肢と同じく「フィールド定義の周辺にあるメタデータ」が入口になります。sys_number(採番)、sys_security_acl(権限)、sys_ui_policy(UIポリシーによる動的な出し分け)あたりです。とくに UI ポリシーや Client Script は、sys_choice の静的な候補を条件によってさらに絞る役割を持つので、選択肢の挙動を最後まで追うなら次に押さえたいテーマです。


    用語

    • PDI:Personal Developer Instance(無料の検証用インスタンス)

    • OOTB:Out of the Box(初期設定のまま)

    • sys_choice:選択肢(Choice list)の実体テーブル(Choice Lists)。super_class は空で、独立した System テーブルです。

    • value:DBに保存される内部値(例 2)/ label:ユーザーに見える表示名(例 In Progress)

    • sequence:ドロップダウンの並び順 / inactive:候補から外すフラグ

    • dependent_value:別フィールドの値に連動する依存選択肢の親値

    • element:sys_choice 上で「どのフィールドの選択肢か」を表す内部名

    自分の環境で確認するときのURL

    先頭に自分のインスタンスを付けると、同じ画面が開けます。

    選択肢一覧(state)    : sys_choice_list.do?sysparm_query=name=incident^element=state^inactive=false^ORDERBYsequence
    選択肢一覧(contact)  : sys_choice_list.do?sysparm_query=name=incident^element=contact_type^ORDERBYsequence
    依存選択肢の例        : sys_choice_list.do?sysparm_query=name=cmdb_ci_hardware^element=hardware_substatus^ORDERBYdependent_value^ORDERBYsequence
    辞書の choice 制御値   : sys_dictionary_list.do?sysparm_query=name=incident^element=state
    親テーブルの選択肢    : sys_choice_list.do?sysparm_query=name=task^element=impact

    検証範囲とおことわり

    • 確認した範囲:sys_choice の固有フィールド数(18)、総件数(18,662)、language=en(14,863)、inactive=true(4,352)、dependent_value あり(565)、super_class が空、incident の state(6)/category(6)/contact_type(7)/close_code(10)/hold_reason(4)/notify(3) の件数、incident.state と contact_type のラベル、辞書 choice の制御値(incident/task の state=3)、cmdb_ci_hardware.hardware_substatus の依存選択肢(25件)、task の impact/urgency(各3件)、priority に選択肢が無いこと。すべて PDI(Zurich / 2026-06-22 確認)の OOTB で取得しています。

    • 確認していない範囲:sys_choice_set(選択肢セット)の細部、全言語の翻訳網羅状況、辞書 choice 値の 0/1/3 以外の意味、Choice 型と Reference 型・Field Style の差の内部実装、ドメイン分離下での選択肢の見え方、Now Experience(次世代UI)でのレンダリング差。これらは別途確認が必要です。

    今回は PDI の OOTB(初期設定)を前提に、選択肢の実体テーブル sys_choice の構造を確認しました。実環境では、カスタマイズで追加・無効化された選択肢や、多言語ラベルの有無により、件数や見え方が変わります。

     
     
    ServiceNowのPDI(Personal Developer Instance)を実機で観察して、Noteに記事を書いています 実装・運用・教育の現場視点で淡々と記録中です リクエストがあればその記事も書きます 個人の観察日記です

    あなたへのおすすめ