Server Scripting · Lesson 2.5

GlideAggregate in ServiceNow: count and total records fast

  • Beginner
  • 16 min read
  • Tested on Australia
  • Updated 22 Oct 2026
  • 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.

InteractiveCount open Critical incidents two ways
  1. 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)
  2. 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)
  3. 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
  4. 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)
  5. 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)
  6. 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

  1. 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 .
  2. Paste the “Counting per group” example above and select Run script. Note the count for each priority.
  3. Open Incident > Open and group the list by Priority. The numbers in the list should match your output.
  4. Change groupBy('priority') to groupBy('assignment_group') and run it again, to see how many open incidents each team has.

Practice: GlideRecord or GlideAggregate?

PracticeGlideRecord 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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().

  6. Close every resolved incident that has not been updated for 30 days.

    Answer: GlideRecord. Closing means changing each matching record, which GlideAggregate cannot do.

  7. 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.

  8. 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?

Spotted a mistake, or a step that looks different in your instance?

Send feedback

Ideas, mistakes, anything confusing: it all comes straight to us, and we read every message.

What is it about?

The page you are on is sent with your message. Privacy