
はじめてのGlideRecord ―1行検索から更新まで実機で動かす
これまでこのノートでは、ServiceNow の「テーブルの構造を読む」記事をたくさん書いてきました。今回から趣向を変えて、実際にコードを書いて動かすシリーズを始めます。第1回のテーマは GlideRecord(グライドレコード) です。
GlideRecord は、ServiceNow でサーバサイドのスクリプトを書くとき、いちばん最初に覚える道具です。Business Rule でも Script Include でも Flow のスクリプトステップでも、テーブルのデータを読み書きする場面ではほぼ必ず登場します。逆に言えば、GlideRecord の「検索して・読んで・書く」がひととおり分かれば、ServiceNow 開発の入り口はくぐれたようなもの。それくらい中心にある道具だと思ってください。
この記事では、その GlideRecord を Scripts - Background(スクリプト実行画面) で実際に動かしていきましょう。難しい準備は要りません。コードをコピーして貼り付け、ボタンを押すだけ。しかも、この記事に載っている結果はすべて筆者の PDI で本当に流したときの画面です。読みながら同じコードを手で動かせば、あなたの環境でもほぼ同じ結果が出るはずです。
この記事は、ServiceNow の PDI(Personal Developer Instance、無料の個人検証環境)Zurich リリース・OOTB(初期設定)で実機実行した記録です。コードの出力はすべて Scripts - Background で流した結果で、件数は REST でも二重確認しました(確認日 2026-07-18)。本番環境では絶対に試さないでください(理由はすぐ下で説明します)。
GlideRecord はどこで使うのか
コードを見る前に、GlideRecord がどこで役立つのかを軽く押さえておきましょう。ServiceNow の自動化やカスタマイズは、たいてい「あるテーブルのレコードを探し・値を読み・何かを判断し・別のレコードを直す」という形をしています。この「探して・読んで・直す」を担うのが GlideRecord です。
たとえば、Business Rule で「インシデントが保存されたら、同じ Caller の未解決の件数を数えて、多ければフラグを立てる」を書くとします。このとき「同じ Caller の未解決を数える」のが GlideRecord の仕事です。Script Include に共通処理として束ねるときも、Scheduled Job で夜間バッチを回すときも、中心にいるのは同じ GlideRecord。つまり、ここで覚える書き方は、今後どんなスクリプトを書くときにも使い回せる基礎になります。だからこそ、最初にていねいに手を動かしておく価値があります。
Scripts - Background:サーバでコードを1回流せる場所
まずはコードを動かす場所を用意しましょう。ServiceNow には Scripts - Background という、サーバサイドの JavaScript をその場で1回だけ実行できる管理画面が用意されています。左のフィルタ(All メニュー)で「Scripts - Background」を検索すれば開けます。

開くと、いちばん上に赤い文字でこう書かれています。
Running freeform script can cause system disruption or loss of data.
(自由記述のスクリプトを実行すると、システム障害やデータ損失を起こしうる)
これは脅しではありません。Scripts - Background で流したコードは、その場で本番同様に実行されます。deleteMultiple() を1行書けば、条件に合うレコードが本当に全部消えます。取り消しの確認ダイアログは出ません。だからこの画面は、必ず PDI(自分専用の検証インスタンス)で練習してください。会社の本番インスタンスでは絶対に触らない。これが最初の、そして最大の約束事です。
画面の下には「Run script」ボタンと、いくつかのチェックボックスが並んでいます。今回はすべて既定のまま使いましょう。とくに Record for rollback(ロールバック記録)が既定でオンになっている点は覚えておくと安心です。練習で作ったり変えたりしたデータを、あとから履歴経由で元に戻せるからです。失敗しても取り返しがつく、と分かっていれば、思い切って手を動かせます。
PDI をまだ持っていない場合は、ServiceNow の Developer サイト(developer.servicenow.com)から無料で1つ払い出せます。この記事のコードはすべて OOTB の初期データで動くので、払い出したばかりの素のインスタンスでそのまま試せます。
GlideRecord とは:テーブル1枚を操作する道具
GlideRecord は、ひとことで言うと 「1つのテーブルを操作するためのオブジェクト」 です。incident テーブルを触りたいなら incident 用の GlideRecord を、sys_user を触りたいなら sys_user 用を作ります。
var gr = new GlideRecord('incident');この1行の意味は「これから incident テーブルを操作します」という宣言です。まだ検索も取得もしていません。この gr に対して条件を足したり、1件だけ取ってきたり、値を書き換えて保存したり。そうした操作を続けていく形になります。まずはいちばん簡単な「1件だけ取ってくる」から始めましょう。
まず1件だけ取る:get()
「INC0000001 という番号のインシデントを1件だけ見たい」。こういう 1件を名指しで取るときは get() を使います。
var gr = new GlideRecord('incident');
if (gr.get('number', 'INC0000001')) {
gs.info('番号 : ' + gr.getValue('number'));
gs.info('短い説明 : ' + gr.getValue('short_description'));
gs.info('状態(値) : ' + gr.getValue('state'));
gs.info('状態(表示) : ' + gr.state.getDisplayValue());
gs.info('優先度(表示): ' + gr.priority.getDisplayValue());
} else {
gs.info('INC0000001 は見つかりませんでした');
}このコードを Scripts - Background に貼り付け、「Run script」を押してみてください。画面の下に、gs.info() で出した文字が「\*\*\* Script:」に続いて並びます。

get('number', 'INC0000001') は「number が INC0000001 のレコードを1件掴む」という指示です。見つかれば true を返し、gr はそのレコードの中身を持った状態になります。だから if の内側で各項目を読めるわけですね。見つからなければ false なので、else に落ちます。この「見つかったかどうかで分岐する」書き方が定石です。gs.info() は、そのメッセージをログに出す関数だと思っておけば大丈夫。実行結果の確認に重宝します。
なお、番号ではなく sys_id(レコードの内部ID) が分かっているなら、gr.get(sys_id) と引数1つで書けます。実務では sys_id を渡して取る場面のほうがむしろ多いくらいです。get() は「引数1つなら sys_id、引数2つなら 項目名+値」と覚えておきましょう。
値の取り方には種類がある
上のコードで、状態を2通りの書き方で出したのに気づいたでしょうか。getValue('state') は 7、gr.state.getDisplayValue() は Closed でした。同じ項目なのに、返ってくる値が違います。

ServiceNow の選択肢(Choice)項目は、画面には「Closed」と表示されますが、データベースには 7 という値で保存されています。getValue() は保存されている生の値を返し、getDisplayValue() は画面に出る表示ラベルを返す、という違いです。ログに人が読める文言を出したいなら表示値、別の条件判定に使う内部値がほしいなら生値、というふうに使い分けます。
この違いを知らないと「7 って何?」と最初に必ずつまずくところなので、早めに押さえておくと後がずっと楽になります。ちなみに gr.short_description のような普通の文字列項目は、生値も表示値も同じなので、あまり気にしなくて構いません。生値と表示値が食い違うのは、Choice・参照(Reference)・日付といった「裏に別の値を持つ項目」です。
条件で複数取る:addQuery → query → while(next())
次は「条件に合うものをまとめて取る」です。ここが GlideRecord のいちばんの基本になります。「いまアクティブで、優先度が Critical のインシデントを一覧したい」を書いてみましょう。
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('priority', 1); // 1 = Critical
gr.orderBy('number');
gr.setLimit(5);
gr.query();
gs.info('取得した件数: ' + gr.getRowCount());
while (gr.next()) {
gs.info(gr.getValue('number') + ' | ' + gr.priority.getDisplayValue() + ' | ' + gr.getValue('short_description'));
}実行すると、こうなりました。

流れを分解してみます。addQuery('active', true) と addQuery('priority', 1) で条件を2つ足しました。addQuery は何本でも重ねられて、すべて AND でつながります。orderBy で並び順を、setLimit(5) で最大5件を指定しました。ここまでは「こういう条件で探してね」というメモを準備しているだけ、という点が大事です。
そして query()。query() を呼んで、初めて検索が実行されます。それまでの addQuery は条件を積んでいるだけで、データベースにはまだ何も問い合わせていません。query() が引き金になって、ようやく検索が走るわけです。ここを忘れると結果が0件になるので、あとで「つまずき」の章でもう一度触れます。
検索が走ったら、while (gr.next()) で結果を1行ずつ回します。next() は「次の行へカーソルを進める」メソッドで、進めれば true、もう行がなければ false を返す仕組みです。だから while の条件に置くと、全行を1件ずつ処理して、最後は自然に抜けてくれます。この一連の流れを図にすると、こんな形です。

左の道が「複数取るときの流れ」、右の紫が「1件だけなら get() で一発」です。GlideRecord のコードは、ほぼこの形の組み合わせでできています。まずこの1本の道を頭に入れてしまえば、あとは条件や中の処理を差し替えるだけ。応用はいくらでも効きます。
なお、addQuery は等しい条件だけでなく、gr.addQuery('priority', '<=', 2) のように演算子も渡せます。「一覧画面で作った絞り込みを、そのままコードに持ち込む」addEncodedQuery という便利な書き方もありますが、これは次回のテーマにとっておきましょう。
件数を数える:getRowCount()
「アクティブなインシデントは今いくつある?」のように件数だけ知りたいときは、query してから getRowCount() を読みます。
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.query();
gs.info('active な incident の件数: ' + gr.getRowCount());
筆者の PDI では 39件 でした(この数はインスタンスによって変わります)。
ひとつ気をつけたい点。この書き方は、件数を知るためにいったん全行を取ってきてから数えています。数十件なら気になりませんが、何万件もあるテーブルで同じことをやると、無駄が大きくなります。大量データの件数集計には GlideAggregate という専用の道具があって、そちらのほうが速く、サーバにもやさしい正攻法です。GlideAggregate は次回にじっくり扱います。今回は「query してから getRowCount で件数が取れる」とだけ覚えておけば十分です。
更新する:get → setValue → update()
読むのに慣れたら、いよいよ書き込みです。ここでは本番データを触らないよう、練習用のレコードを1件その場で作って、それを更新するという形にしました。
// 1) 練習用のレコードを1件つくる(あとで消してOK)
var ins = new GlideRecord('incident');
ins.initialize();
ins.setValue('short_description', 'GlideRecord 練習用レコード');
ins.setValue('priority', 4);
var sysId = ins.insert();
gs.info('作成しました: ' + ins.getValue('number') + ' (sys_id=' + sysId + ')');
// 2) いま作ったレコードを取得して更新する
var gr = new GlideRecord('incident');
if (gr.get(sysId)) {
gr.setValue('short_description', 'GlideRecord で更新しました');
gr.setValue('comments', '練習: コメントを1行追記');
gr.update();
gs.info('更新しました: ' + gr.getValue('number'));
gs.info('更新後の説明: ' + gr.getValue('short_description'));
}実行結果です。上に表が出て、下に文字が並びます。

まず下の文字を見てください。INC0010022 という番号でレコードが1件作られ、その説明が「GlideRecord で更新しました」に変わっています。作成から更新まで、ちゃんと通りました。前半の initialize() は「新しい空のレコードを1件用意する」という意味で、insert する前のおまじないだと思っておけば大丈夫です。
ここで押さえたいポイントが2つあります。1つめ、setValue() はメモリ上の値を書き換えるだけで、この時点ではまだデータベースは変わっていません。update()(新規なら insert())を呼んで、初めて保存されます。「値をセットしたのに DB が変わらない」というトラブルは、たいてい update の呼び忘れが原因です。読むだけなら不要、書くなら必ず update()、と紐づけて覚えましょう。
2つめ、画面の上に出た表に注目してください。「Record for rollback」の記録として、insert が incident に1件、update が incident に1件入っています。これこそが「本当に DB へ書き込まれた」動かぬ証拠です。ついでに通知(notification)やエスカレーション(escalate)のレコードも一緒に動いていて、たった1件のインシデントを作るだけで、裏では色々な処理が連動しているのが見て取れます。この連動を眺められるのも、Scripts - Background で手を動かす面白さですね。
作った練習レコード(この例では INC0010022)は、不要なら消して構いません。Record for rollback がオンなので、Scripts - Background の実行履歴からロールバックして、丸ごと元に戻すこともできます。安心して何度でも試してください。
よくあるつまずき
最後に、最初にハマりやすい点をまとめておきます。どれも一度は通る道なので、先回りして知っておくと安心です。
query() を書き忘れる:addQuery だけ書いて query() を呼ばないと、検索そのものが走りません。next() は最初から false になり、結果はいつも0件。「なぜか取れない」と思ったら、まず query() があるかを確認しましょう。
update() を書き忘れる:setValue で値を変えても、update() がなければ保存されません。画面には何のエラーも出ないので、気づきにくいのが厄介なところ。書き込む処理には必ず update()(新規なら insert())を添えてください。
生値と表示値を混同する:state が 7 なのか Closed なのか。条件判定に使うのは生値(getValue)、人に見せる表示に使うのは表示値(getDisplayValue)です。ここは最初の関門なので、迷ったら前の章の表に戻りましょう。
本番でいきなり試す:くり返しになりますが、Scripts - Background は本番でもそのまま動いてしまいます。練習は必ず PDI で。これだけは徹底してください。
よく使うメソッド早見表
今回登場したメソッドを一覧にしておきます。まずはこれだけ手になじませれば、しばらく困りません。

まとめ
GlideRecord の基本は、「テーブルを決め・条件を足し・query で検索し・next で1件ずつ回す」。1件だけなら get() で一発。値は getValue(生値)と getDisplayValue(表示値)を使い分ける。そして書き込みは setValue してから update() で保存する。今日動かしたのはこれだけですが、ServiceNow 開発で書くコードの大半は、この組み合わせの応用にすぎません。
読むだけで終わらせず、実際に Scripts - Background で流してみてください。コードをコピーして、ボタンを押して、記事と同じ結果が出る。その体験を一度くぐると、次からのコードが不思議とすらすら読めるようになります。ぜひ自分の PDI で、今日のコードをそのまま動かしてみてください。
次回は、今回さらっと流した 件数集計を正しく速くやる GlideAggregate を掘り下げます。あわせて、addEncodedQuery(画面で作った絞り込み条件を、そのままコードに持ち込む方法)も取り上げる予定です。一覧画面で組んだフィルタを、コピー&ペーストでスクリプトの条件にできます。地味ですが、よく効く小技です。
その他、実機で確認した記録
用語
GlideRecord … 1つのテーブルを検索・読み書きするためのサーバサイドの標準オブジェクト
Scripts - Background … サーバサイドJSをその場で1回実行する管理画面(/sys.scripts.do)
get() … 条件に合う1件を掴む(見つかれば true)
query() … addQuery で足した条件で検索を実行する(呼ぶまで走らない)
next() … 結果を次の1行へ進める(無ければ false)
getValue / getDisplayValue … 生値 / 画面の表示ラベル
setValue → update() … 値を書き換え、DB へ保存する
initialize / insert … 空レコードを用意し、新規に1件作る
実行に使ったスクリプト
本文のコードは、そのまま Scripts - Background に貼れば動きます(実行は必ず PDI で)。出力はすべて筆者の PDI(dev / Zurich / OOTB)で流した実画面です。
調べるときの入口URL(先頭に自分のインスタンスを付ける)
/sys.scripts.do … Scripts - Background(今回コードを流した画面)
/syslog_list.do?sysparm_query=ORDERBYDESCsys_created_on … System Log(gs.info の出力も辿れる)
/incident_list.do?sysparm_query=active=true^priority=1 … 今回 query で取った条件を画面で確認
/sys_db_object_list.do … テーブル定義の一覧(GlideRecord に渡すテーブル名の確認)
実測の要点(再掲)
INC0000001 … state 生値=7 / 表示=Closed / priority=1 - Critical(get で取得)
active=true^priority=1 の上位5件 … INC0000002 / 0003 / 0007 / 0015 / 0016
active な incident … 39件(getRowCount。REST の count でも 39 を確認)
更新 … 練習レコードを insert → update。rollback 表に incident の insert=1 / update=1 を確認
検証範囲とおことわり
実行は PDI(Zurich / OOTB)の Scripts - Background・scope=global のみ。本番やスコープドアプリでの挙動差は範囲外です。
件数集計の性能や GlideAggregate との比較は次回に回します(本記事は検索と更新の基本のみ)。
練習レコードの番号(INC0010022)は執筆時のものです。読者の環境では別の番号になります。