Counting records in ServiceNow correctly requires understanding GlideAggregate. Using GlideRecord to count records works but is significantly less efficient — GlideRecord loads every matching record into memory before counting. GlideAggregate pushes the COUNT operation to the database and returns only the number.
Basic COUNT
// Count all active incidents
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT');
ga.addEncodedQuery('active=true');
ga.query();
ga.next();
var count = ga.getAggregate('COUNT');
gs.log('Active incidents: ' + count);
// Output: "Active incidents: 247"
COUNT with groupBy — Counts Per Group
The most useful pattern — count records broken down by a field value:
// Count incidents grouped by priority
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT');
ga.addEncodedQuery('active=true');
ga.groupBy('priority');
ga.orderBy('priority');
ga.query();
while (ga.next()) {
var priority = ga.getDisplayValue('priority');
var count = ga.getAggregate('COUNT');
gs.log(priority + ': ' + count);
}
// Output:
// "1 - Critical: 3"
// "2 - High: 12"
// "3 - Moderate: 48"
COUNT on a Specific Field
You can count a specific field rather than all rows. This is useful for counting non-null values:
// Count incidents where assigned_to is populated
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT', 'assigned_to');
ga.addEncodedQuery('active=true');
ga.query();
ga.next();
var assignedCount = ga.getAggregate('COUNT', 'assigned_to');
gs.log('Assigned incidents: ' + assignedCount);
COUNT DISTINCT — Unique Values
// Count how many unique assignment groups have active incidents
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT DISTINCT', 'assignment_group');
ga.addEncodedQuery('active=true');
ga.query();
ga.next();
var uniqueGroups = ga.getAggregate('COUNT DISTINCT', 'assignment_group');
gs.log('Groups with active incidents: ' + uniqueGroups);
Multiple Aggregates in One Query
// Get count, min, and max in a single database call
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT');
ga.addAggregate('MIN', 'sys_created_on');
ga.addAggregate('MAX', 'sys_created_on');
ga.addEncodedQuery('active=true^priority=1');
ga.query();
if (ga.next()) {
gs.log('Count: ' + ga.getAggregate('COUNT'));
gs.log('Oldest: ' + ga.getAggregate('MIN', 'sys_created_on'));
gs.log('Newest: ' + ga.getAggregate('MAX', 'sys_created_on'));
}
GlideAggregate vs GlideRecord for Counting
| Method | How it works | Performance |
|---|---|---|
| GlideAggregate COUNT | Database-level COUNT(*) | Extremely fast — one DB call |
| GlideRecord + getRowCount() | Loads all matching records | Slow on large tables |
| GlideRecord loop counter | Iterates every record | Slowest — avoid entirely |
On a table with 100,000 records, GlideAggregate COUNT returns in milliseconds. GlideRecord getRowCount() may take several seconds.
Using GlideAggregate in a Script Include
// Reusable function to get record count
function getRecordCount(table, encodedQuery) {
var ga = new GlideAggregate(table);
ga.addAggregate('COUNT');
if (encodedQuery) {
ga.addEncodedQuery(encodedQuery);
}
ga.query();
if (ga.next()) {
return parseInt(ga.getAggregate('COUNT'));
}
return 0;
}
// Usage
var p1Count = getRecordCount('incident', 'active=true^priority=1');
gs.log('P1 count: ' + p1Count);
Common Mistake — Calling getAggregate Before next()
// ❌ Wrong — getAggregate called before next()
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT');
ga.query();
var count = ga.getAggregate('COUNT'); // Returns null
// ✅ Correct — always call next() first
var ga = new GlideAggregate('incident');
ga.addAggregate('COUNT');
ga.query();
ga.next(); // Must call this before getAggregate
var count = ga.getAggregate('COUNT'); // Returns the count