Server Scripting · Lesson 2.5
GlideAggregate in ServiceNow: count and total records fast
- GlideAggregate
- GlideRecord
Why GlideAggregate exists
You already know GlideRecord: you query a TableA collection of records of the same kind, like the Incident table that stores every incident.Learn more , and loop through the records that come back. It is the right tool when you need the records themselves, to read their fields or to change them.
But very often you do not want the records at all. You want a number. “How many Critical incidents are open?” “How many incidents does each team have?” Counting with GlideRecord means loading every matching record, all its fields included, just to add one to a counter for each. On a small PDI that is instant. On a real instance with hundreds of thousands of records, it is slow, and it slows everyone else down while it runs.
GlideAggregate hands the counting to the database, which is built for exactly that, and gets back a single number.
Your first count
The code looks almost exactly like a GlideRecord query, with two differences: addAggregate('COUNT') before the query, and getAggregate('COUNT') to read the answer.
var ga = new GlideAggregate('incident');
ga.addActiveQuery(); // only active incidents
ga.addQuery('priority', 1); // only 1 - Critical
ga.addAggregate('COUNT'); // ask for the count, not the records
ga.query();
var count = 0;
if (ga.next()) {
count = parseInt(ga.getAggregate('COUNT'), 10);
}
gs.info('Open Critical incidents: ' + count);
The filters work exactly like GlideRecord’s: addQuery, addActiveQuery and addEncodedQuery all behave the same. After query(), the single row of results is read with next(), just like a record.
One detail matters: getAggregate() returns the number as text, such as "3", not as a number. That is why the example wraps it in parseInt(..., 10), which turns the text into a real number you can do maths with.
Press Play to see the difference between the two approaches on a busy instance.
Step 1 of 6 · Ask
Way 1: GlideRecord asks for the records
On a busy instance with 40,000 incidents, 1,250 are active and Critical. A GlideRecord query asks the database for every one of those records, with all their fields.
var gr = new GlideRecord('incident'); gr.addActiveQuery(); gr.addQuery('priority', 1); gr.query();- Method
- GlideRecord
- Rows sent back
- (waiting)
- Work done by
- Your script
- Answer
- (not yet)
Step 2 of 6 · Bring back
All 1,250 records travel to your script
The database finds the matching incidents and sends all 1,250 of them back, each one a full record with dozens of fields. Your script now has to step through them.
- Method
- GlideRecord
- Rows sent back
- 1,250 full records
- Work done by
- Your script
- Answer
- (not yet)
Step 3 of 6 · Count
The script counts them one by one
A while loop calls next() 1,250 times and adds one to a counter each time. It gets the right answer, but it moved a lot of data just to produce one number.
var count = 0; while (gr.next()) { count++; }- Method
- GlideRecord
- Rows sent back
- 1,250 full records
- Work done by
- Your script, 1,250 loops
- Answer
- 1250
Step 4 of 6 · Ask
Way 2: GlideAggregate asks for the count
Now the same question with GlideAggregate. The query looks almost the same, with one extra line: addAggregate('COUNT') tells the database you only want the total.
var ga = new GlideAggregate('incident'); ga.addActiveQuery(); ga.addQuery('priority', 1); ga.addAggregate('COUNT'); ga.query();- Method
- GlideAggregate
- Rows sent back
- (waiting)
- Work done by
- The database
- Answer
- (not yet)
Step 5 of 6 · Database
The database does the counting
Databases are built to count. It works out the total itself, close to where the data lives, without sending any incident records anywhere.
- Method
- GlideAggregate
- Rows sent back
- (counting inside the database)
- Work done by
- The database
- Answer
- (not yet)
Step 6 of 6 · Answer
One row comes back with the answer
A single row comes back holding the number. One call to next() and one call to getAggregate(), and you have the same answer as before, without moving 1,250 records.
ga.next(); var count = parseInt(ga.getAggregate('COUNT'), 10);- Method
- GlideAggregate
- Rows sent back
- 1 row
- Work done by
- The database
- Answer
- 1250
Counting per group
Often you want a count for each value of a FieldOne piece of information on a record, like an incident's Priority or Short description.Learn more : incidents per priority, per state or per Assignment groupThe team responsible for working on a task, such as the Network team for a network incident.Learn more . Add groupBy and loop through the results. Each row is one group:
var ga = new GlideAggregate('incident');
ga.addActiveQuery();
ga.addAggregate('COUNT');
ga.groupBy('priority');
ga.query();
while (ga.next()) {
gs.info(ga.priority.getDisplayValue() + ': ' + ga.getAggregate('COUNT'));
}
On a PDI this prints one line per priority, such as 1 - Critical: 7, 2 - High: 12 and so on. Inside the loop, ga.priority is the group’s value, read the same way as a field on a GlideRecord.
Totals, averages, highest and lowest
COUNT is the one you will use most, but GlideAggregate can also calculate with the values of a field. Name the field as a second argument, both when you add the aggregate and when you read it:
| Aggregate | What it returns | Example |
|---|---|---|
COUNT |
How many records match | Open incidents |
SUM |
The total of a number field | Total reassignments across all incidents |
AVG |
The average of a number field | Average reassignment count per incident |
MIN |
The lowest value | The oldest opened date |
MAX |
The highest value | The most recent update |
var ga = new GlideAggregate('incident');
ga.addQuery('state', 6); // 6 = Resolved
ga.addAggregate('AVG', 'reassignment_count');
ga.query();
if (ga.next()) {
gs.info('Average reassignments: ' + ga.getAggregate('AVG', 'reassignment_count'));
}
GlideRecord or GlideAggregate?
| GlideRecord | GlideAggregate | |
|---|---|---|
| What comes back | The records, with all their fields | Numbers about the records |
| Can it change records? | Yes, with update(), insert() and deleteRecord() |
No, it only reads |
| Best for | Reading or changing specific records | Counts, totals, averages and per-group numbers |
| On a big table | Gets slower the more records match | Stays fast, because the database does the work |
See it in your instance
- Open System Definition > Scripts - Background in your PDI (Personal Developer Instance)A free ServiceNow instance you get from developer.servicenow.com to practise on safely.Learn more .
- Paste the “Counting per group” example above and select Run script. Note the count for each priority.
- Open Incident > Open and group the list by Priority. The numbers in the list should match your output.
- Change
groupBy('priority')togroupBy('assignment_group')and run it again, to see how many open incidents each team has.
Practice: GlideRecord or GlideAggregate?
GlideRecord is for working with records: reading their fields or changing them. GlideAggregate is for numbers about records: counts, totals, averages, highest and lowest. Which one fits?
How many incidents are open and Critical right now?
Answer: GlideAggregate. You need one number, not the records. COUNT asks the database for exactly that.
Add a work note to every open Critical incident.
Answer: GlideRecord. You have to change each record, so you need each record. Loop through them with GlideRecord and update them.
How many open incidents does each assignment group have?
Answer: GlideAggregate. A count per group: groupBy on assignment_group plus a COUNT gives one row per group with its total.
What is the average reassignment count of incidents resolved this month?
Answer: GlideAggregate. AVG on the reassignment_count field returns the average directly, calculated by the database.
Read the short description and caller of INC0010061.
Answer: GlideRecord. You want field values from one specific record. GlideRecord gets it, for example with get().
Close every resolved incident that has not been updated for 30 days.
Answer: GlideRecord. Closing means changing each matching record, which GlideAggregate cannot do.
Find the earliest opened date among all open incidents.
Answer: GlideAggregate. MIN on opened_at returns the lowest value in one row, without loading the incidents.
Build a list of the callers of all open Critical incidents to include in an email.
Answer: GlideRecord. You need a field from each record, the caller, so you need the records themselves.
What’s next
You have now learned every piece of the Server Scripting module: Business Rules and their types, conditions and changes, Script Includes, and GlideAggregate. The last lesson, Challenge: warn about Critical overload, puts them all together in one small, real feature that you build in your own PDI.
Quick check
A GlideAggregate COUNT query finds 3 matching incidents. What does ga.getAggregate('COUNT') + 1 give?
Show answer
'31'
getAggregate() returns the count as text, “3”. Adding 1 to text joins them into “31”. Use parseInt(ga.getAggregate('COUNT'), 10) + 1 to get the number 4.
Was this lesson helpful?
Thanks! Glad it helped. 🙌
Thank you. We will use this to improve the lesson.
Spotted a mistake, or a step that looks different in your instance?