The N+1 query problem is ServiceNow's most common performance killer β€” you execute one query to get a list of records, then execute N additional queries inside the loop to fetch related data. What looks like innocent lookups becomes hundreds of database hits that bring your instance to its knees. The platform doesn't warn you about this; it just starts timing out your scripts when the query volume gets ridiculous. The solution is always the same: collect all the IDs you need first, then use addQuery('field', 'IN', ids) to batch the lookup into a single query.

When to use this

  • When you need to enrich records with data from related tables in a Business Rule or Script Include
  • When processing a bounded set of records where you control the input size (under 1000 records)
  • When you need multiple field values from the related records, not just existence checks
  • When running in Scheduled Jobs that process batches of records for data cleanup or reporting

When NOT to use this

  • Don't use this in Client Scripts β€” use GlideAjax to call a Script Include that does the batching server-side
  • Don't use this when you only need counts or existence checks β€” use GlideAggregate or GlideRecord.hasNext()
  • Don't use this for unbounded result sets β€” you'll hit memory limits before you hit query limits
  • Don't batch reference field lookups that are already cached by getDisplayValue() β€” you're optimizing something that's already fast

Key behaviors and gotchas

  • The IN operator expects a comma-separated string, not an array β€” always use join(',')
  • Reference fields return GlideElementReference objects β€” call toString() to get the sys_id for comparisons
  • Empty reference fields and deleted records will create gaps in your lookup map β€” always provide fallback values
  • ACLs still apply to batch queries β€” records the user can't read will silently disappear from results
  • Domain separation affects IN queries β€” records in different domains may not be visible even with valid sys_ids
  • Large IN clauses (over 1000 values) can hit database query length limits β€” chunk them if needed
⚠️

Building lookup maps with sys_id keys requires toString() on both sides β€” GlideRecord sys_id fields and reference field values are objects, not strings. Missing toString() calls create lookup failures that are invisible until you test with real data.

Free Newsletter

Enjoying this? Get one deep-dive per week.

Join 1,000+ ServiceNow pros β€” scripts, GlideRecord patterns, Flow Designer techniques, and career moves. Free.

No spam Β· Unsubscribe anytime