1. Querying with GlideRecord
A GlideRecord object represents a table, and moving through query
results follows the same three-step shape every time: build a
GlideRecord on the table, add query conditions, call
query(), then loop with next().
var gr = new GlideRecord('incident');
gr.addQuery('priority', '1');
gr.addQuery('state', '!=', '6'); // not Resolved
gr.query();
while (gr.next()) {
gs.info('Open P1: ' + gr.number + ' — ' + gr.short_description);
}
gr.number and gr.short_description aren't plain
strings — they're GlideElement objects that stringify sensibly in
most contexts but carry extra methods (like changesFrom(),
covered in Week 5). For a single expected record, get() is a
shortcut that skips the query/next dance:
var gr = new GlideRecord('incident');
if (gr.get('sys_id', someSysId)) {
gs.info('Found: ' + gr.number);
}
2. Encoded Queries
Every list view's filter compiles down to an encoded query — a
compact string representation you can copy straight out of the URL bar
(right after "sysparm_query=") and paste into a script with
addEncodedQuery(). This is often faster than hand-writing several
addQuery() calls, and it guarantees the script matches exactly what
you were looking at on screen.
var gr = new GlideRecord('incident');
gr.addEncodedQuery('priority=1^state!=6^assignment_group=' + groupSysId);
gr.query();
3. Create, Update & Delete
var gr = new GlideRecord('incident');
gr.initialize();
gr.short_description = 'Printer offline on 4th floor';
gr.priority = 3;
var newSysId = gr.insert();
var gr = new GlideRecord('incident');
if (gr.get(sysId)) {
gr.state = 2; // In Progress
gr.update();
}
var gr = new GlideRecord('incident');
if (gr.get(sysId)) {
gr.deleteRecord();
}
gr.field = value and gr.setValue('field', value) behave the same for most field types, but setValue() is the safer choice when the field name is a runtime string (a variable), since gr[fieldNameVar] = value doesn't reliably work the same way across all GlideRecord field types.
4. GlideAggregate: Counts & Sums Without Looping
Never loop over a GlideRecord result just to count rows or sum a field —
GlideAggregate pushes that work down to the database instead of
pulling every record across into script.
var ga = new GlideAggregate('incident');
ga.addQuery('state', '1'); // New
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
gs.info('New incidents: ' + ga.getAggregate('COUNT'));
}
5. Common Pitfalls
The single most common GlideRecord bug: querying inside a loop. A query executed once per iteration of an outer loop turns an O(n) operation into something that scales far worse, and on a busy instance it's a reliable way to time out a Business Rule.
// BAD: one query per incident, inside the loop
var incidents = new GlideRecord('incident');
incidents.query();
while (incidents.next()) {
var user = new GlideRecord('sys_user');
user.get(incidents.caller_id); // a fresh query, every single iteration
gs.info(user.name);
}
Dot-walking (incidents.caller_id.name, from Week 2) reads the
already-joined reference data instead — no extra query needed for a single
field read.
6. Hands-on Exercise
Write a background script that reports and cleans up test data
Use Scripts — Background (System Definition > Scripts - Background) for this exercise; it's the fastest way to run one-off GlideRecord scripts while learning.
Requirements:
- Query all incidents with
short_descriptioncontaining "test" (useaddQuery('short_description', 'CONTAINS', 'test')) and log each one's number. - Use GlideAggregate to log a total count of matching records before you touch anything.
- Insert three new incidents with short_description values containing "test cleanup exercise."
- Write a second script that queries for exactly those three (by short_description) and deletes them with
deleteRecord(). - Re-run your GlideAggregate count script to confirm the count returned to its original value.
Always run a GlideAggregate COUNT before running any script that deletes records in bulk — it's a cheap sanity check on exactly how many rows your query actually matches before you commit to a destructive operation.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What are the three steps every basic GlideRecord query follows?
What are the three steps every basic GlideRecord query follows?
Build a GlideRecord object on a table, add one or more query conditions with addQuery() (or addEncodedQuery()), call query() to execute, then loop through matches with next(). Skipping query() or next() is the most common first mistake — the record set doesn't populate until both are called.
Q2
When is get() a better choice than the query()/next() pattern?
When is get() a better choice than the query()/next() pattern?
When you're fetching exactly one record you expect to exist, identified by a known field (often sys_id). get() combines the query and the first next() into one call and returns a boolean indicating whether a match was found, which is more direct than the loop form for a single expected record.
Q3
Why should you use GlideAggregate instead of looping over a GlideRecord just to count rows?
Why should you use GlideAggregate instead of looping over a GlideRecord just to count rows?
GlideAggregate pushes the counting or summing work down to the database itself, returning just the aggregate value. Looping over every matching GlideRecord row just to increment a counter in script pulls far more data across than necessary and scales poorly as the table grows.
Q4
What's wrong with querying a table inside a loop that's already iterating over another query's results?
What's wrong with querying a table inside a loop that's already iterating over another query's results?
It turns what should be a small, fixed number of queries into one query per iteration of the outer loop, which scales badly and is a common cause of Business Rules timing out on busy instances. Dot-walking a reference field, or restructuring to fetch related data in one batched query, avoids the repeated per-row queries.