Query vs. Get Method: Understanding the Key Differences






Mastering GlideRecord: Understanding the ‘query()’ and ‘get()’ Methods in ServiceNow



Mastering GlideRecord: Understanding the ‘query()’ and ‘get()’ Methods in ServiceNow

Hey there, fellow ServiceNow enthusiast! Ever found yourself scratching your head, wondering which GlideRecord method to use when you need to pull data from your instance? You’re not alone. The world of ServiceNow development, while incredibly powerful, often presents us with choices that, at first glance, might seem similar. Today, we’re diving deep into two fundamental GlideRecord methods: query() and get(). While both are used for retrieving records, they serve distinct purposes and come with their own set of best practices. Let’s unravel their mysteries, explore practical examples, and equip you with the knowledge to wield them like a seasoned pro.

Glide API: Your ServiceNow Scripting Superpower

Before we zero in on query() and get(), let’s set the stage. At the heart of customizing and extending ServiceNow’s capabilities lies the Glide API. Think of it as your Swiss Army knife for server-side scripting. It’s a collection of JavaScript classes and methods designed to let you interact with the platform’s core functionalities – from manipulating dates to managing users, and most importantly, interacting with the underlying database without ever needing to write a single line of SQL!

ServiceNow developers lean heavily on Glide APIs to tailor application behavior, automate processes, and build custom solutions. They provide a safe, structured, and performant way to achieve what would otherwise require direct (and often forbidden) database access. It’s truly what makes ServiceNow so flexible.

A Peek at Server-Side Glide APIs

The Glide family is vast, but some of the most frequently used server-side APIs include:

  • GlideRecord: Our star for today, focusing on database CRUD operations.
  • GlideSystem (gs): Your go-to for system-level operations, logging, and environment details.
  • GlideDate & GlideDateTime: For handling all your date and time manipulation needs.
  • GlideAggregation: When you need to perform aggregate functions (like sum, count, average).
  • GlideElement: For interacting with individual fields (elements) on a GlideRecord.

GlideRecord: The Heartbeat of Data Interaction

Among the pantheon of Glide APIs, GlideRecord stands out as perhaps the most common and critical. If you’re doing any significant server-side scripting in ServiceNow, you’re almost certainly going to be using GlideRecord. It’s a special JavaScript class, native to the ServiceNow platform, and it runs exclusively on the server side.

Its primary role? To perform CRUD (Create, Read, Update, Delete) operations on database tables. Forget about clunky SQL queries; GlideRecord abstracts all that complexity away, allowing you to interact with rows and columns using intuitive JavaScript syntax. This is crucial because ServiceNow explicitly prohibits direct SQL interaction with its database for security and integrity reasons. GlideRecord is the sanctioned gateway.

A Quick Technical Note (and a Word of Caution): Always, always, always test your GlideRecord scripts on a non-production instance first! An incorrectly constructed query, like one with a typo in a field name, can lead to an invalid query. If you then proceed to execute insert(), update(), or deleteRecord() methods on such a “bad query result,” you could inadvertently cause data loss or unintended modifications across your instance. Play it safe!

Key Characteristics of GlideRecord:

  • It’s the most widely used API for database interaction.
  • It operates exclusively on the server side.
  • It generates and executes SQL queries behind the scenes.
  • It facilitates all CRUD operations.

A Glimpse at GlideRecord’s Toolkit

GlideRecord is brimming with methods designed for various tasks. From setting query conditions to manipulating field values, the list is extensive. Just to give you a sense of its breadth, here are some common (and less common) methods you might encounter:

query(), insert(), addQuery(), deleteRecord(), addActiveQuery(), update(), addEncodedQuery(), initialize(), addInactiveQuery(), deleteMultiple(), next(), updateMultiple(), get(), addNullQuery(), setLimit(), addNotNullQuery(), orderBy(), autoSysFields(), orderByDesc(), canCreate(), getRowNumber(), hasNext(), getRowCount(), getDisplayValue(), getValue(), getLink(), isValidRecord(), isValidField(), getTableName(), etc.

It’s a lot, right? Don’t worry, we won’t cover them all today. Our focus is specifically on the nuances between query() and get(), but we will touch upon some auxiliary methods that make working with their results much more effective.

Diving Deep: query() vs. get() – A Tale of Two Retrieval Methods

This is where the rubber meets the road. Both query() and get() are “read” operations within GlideRecord, but they are fundamentally designed for different scenarios. Understanding this distinction is key to writing efficient, maintainable, and correct ServiceNow scripts.

The query() Method: For When You Need a List (or Many Things)

Imagine you’re at a library, and you want to find all books by a particular author, or perhaps all books published in a certain year. You don’t know the exact title of a single book, but you have criteria. That’s precisely what the query() method is for. It’s designed to retrieve a collection of records that match specified conditions. If no conditions are given, it will (attempt to) retrieve all records from the table, which can be a performance nightmare on large tables!

The query() method executes the defined filters or conditions you’ve added to your GlideRecord object. After executing, you typically need to “loop” through the results to process each individual record. This is usually done using a while(gr.next()) loop, where next() advances to the next record in the result set.

Workflow for query():

  1. Initialize GlideRecord: Create a new GlideRecord object for your target table.
  2. Add Conditions (Optional but Recommended!): Use methods like addQuery(), addEncodedQuery(), addActiveQuery(), etc., to filter your results.
  3. Execute the Query: Call query() on your GlideRecord object.
  4. Iterate Through Results: Use a while(gr.next()) loop to process each record found.

Practical Examples with query():

Let’s look at some common scenarios:

1. Retrieving All Records (Use with Caution!)

This is the simplest form but rarely recommended for production code on large tables, as it fetches everything.

var inc = new GlideRecord('incident'); // Initialize GlideRecord for the 'incident' table
inc.query();                           // Execute the query (fetches all incidents if no conditions)

while (inc.next()) {                   // Loop through each incident record
    gs.print('Incident Number: ' + inc.number); // Print the number of each incident
}
// Result: Prints all incident numbers in the Incident Table.
    
2. Adding a Single Condition with addQuery()

Here, we want to find all incidents with a ‘Priority’ of ‘1’ (Critical).

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');         // Add a condition: where priority is 1
inc.query();                           // Execute the query

gs.info('Critical Incidents:');
while (inc.next()) {
    gs.info(inc.number + ' - ' + inc.short_description); // Print number and short description
}
// Result: Prints the numbers and short descriptions of all incidents with Priority 1.
    
3. Combining Multiple Conditions (AND Logic)

When you use multiple addQuery() statements, they are implicitly joined with an ‘AND’ operator. This script finds active, Priority 1, ‘Software’ category incidents.

var inc = new GlideRecord('incident');
inc.addQuery('active', true);           // Condition 1: active is true
inc.addQuery('priority', '1');          // Condition 2: priority is 1
inc.addQuery('category', 'software');   // Condition 3: category is 'software'
inc.query();                            // Execute the combined query

gs.print('Matching Incidents:');
while (inc.next()) {
    gs.print(inc.number + ' (Category: ' + inc.category + ')');
}
// Result: Prints incident numbers that meet ALL three conditions.
    
4. Using addEncodedQuery() for Complex Filters

Encoded queries are powerful. You can build complex queries directly from a list view in ServiceNow, copy the query string, and paste it into your script. This is fantastic for complex ‘AND’/’OR’ logic.

How to get an Encoded Query:

  1. Go to the Incident list view (or any list).
  2. Apply your desired filters (e.g., Active=true, Priority=1, Category=Software).
  3. Right-click the filter breadcrumbs, select “Copy query.”
  4. Paste the copied string into your script.
var encodedQuery = 'active=true^category=software^priority=1'; // Our copied query
var inc = new GlideRecord('incident');
inc.addEncodedQuery(encodedQuery); // Apply the complex query string
inc.query();

gs.print('Incidents via Encoded Query:');
while (inc.next()) {
    gs.print(inc.number + ' - ' + inc.short_description);
}
// Result: Prints incidents matching the complex encoded query string.
    
5. Using Operators with addQuery('field', 'operator', 'value')

GlideRecord supports various SQL-like operators:

  • Numeric/Date: `=`, `!=`, `>`, `>=`, `<`, `<=`
  • Strings (case-insensitive for general query, case-sensitive for literal matching): `=`, `!=`, `IN`, `NOT IN`, `STARTSWITH`, `ENDSWITH`, `CONTAINS`, `DOES NOT CONTAIN`, `INSTANCEOF`

Let’s find active incidents with priority less than or equal to 2, and a short description containing ‘SAP’:

var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Shortcut for 'active=true'
inc.addQuery('priority', '<=', '2'); // Priority is 1 or 2
inc.addQuery('short_description', 'CONTAINS', 'SAP'); // Short description includes 'SAP'
inc.query();

gs.print('SAP-related Critical/High Incidents:');
while (inc.next()) {
    gs.print(inc.number + ' - ' + inc.short_description);
}
// Result: Prints incidents that are active, have priority 1 or 2, and whose short description contains 'SAP'.
    

Example with IN operator:

var categories = ['software', 'hardware'];
var inc = new GlideRecord('incident');
inc.addQuery('category', 'IN', categories); // Category is either 'software' OR 'hardware'
inc.query();

gs.print('Software/Hardware Incidents:');
while (inc.next()) {
    gs.print(inc.getValue('number') + ' - ' + inc.getValue('category') + ' - ' + inc.getValue('short_description'));
}
// Result: Prints incidents where the category is 'software' or 'hardware'.
    
6. Ordering Results with orderBy() and orderByDesc()

To sort your results, use these methods before query():

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.orderByDesc('sys_created_on'); // Order by creation date, descending (latest first)
inc.setLimit(5);                     // Get only the top 5
inc.query();

gs.print('Latest 5 Priority 1 Incidents:');
while (inc.next()) {
    gs.print(inc.number + ' - ' + inc.short_description + ' (' + inc.sys_created_on + ')');
}
// Result: Prints the 5 most recently created Priority 1 incidents.
    
7. Limiting Results with setLimit() and chooseWindow()

setLimit() is straightforward for getting a specific number of records. chooseWindow(start, end) is a bit trickier; it defines a window of records. The first parameter (start) is inclusive, the second (end) is exclusive.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.orderBy('number'); // Order is important for chooseWindow predictability
inc.chooseWindow(2, 5); // Get records at index 2, 3, 4 (0-indexed)
inc.query();

gs.print('Incidents from window (index 2-4):');
while (inc.next()) {
    gs.print(inc.number);
}
// Result: Prints 3 incident numbers, corresponding to the 3rd, 4th, and 5th records in the active, ordered list.
    
8. Checking for Null/Not Null Fields with addNullQuery() and addNotNullQuery()
var inc = new GlideRecord('incident');
inc.addNullQuery('caller_id'); // Find incidents where caller_id is empty
inc.query();

gs.print('Incidents with no Caller:');
while (inc.next()) {
    gs.print(inc.number + ' - ' + inc.short_description);
}

var inc2 = new GlideRecord('incident');
inc2.addNotNullQuery('assignment_group'); // Find incidents that ARE assigned
inc2.setLimit(3); // Just show a few
inc2.query();

gs.print('\nIncidents with Assignment Group (first 3):');
while (inc2.next()) {
    gs.print(inc2.number + ' - ' + inc2.assignment_group.getDisplayValue());
}
// Result: Prints incidents missing a caller, then some incidents with an assignment group.
    
9. Performing Joins with addJoinQuery()

This allows you to find records in one table based on conditions in a related table. For example, finding problems that have associated incidents.

var prob = new GlideRecord('problem');
// Join problem with incident table where problem.opened_by matches incident.caller_id
prob.addJoinQuery('incident', 'opened_by', 'caller_id');
prob.query();

gs.print('Problems with Associated Incidents (via opened_by/caller_id):');
while (prob.next()){
    gs.print(prob.number + ' - ' + prob.short_description);
}
// Result: Displays all problem records that have an associated incident where the opened_by of the problem matches the caller_id of the incident.
    

When to Use query():

  • You need to retrieve zero, one, or multiple records based on specified criteria.
  • You want to iterate through a list of records.
  • You are performing aggregate operations on a set of records (though GlideAggregate might be more suitable for complex aggregations).

The get() Method: When You Know Exactly What You Want (A Single Item)

Now, let's switch gears. Imagine you're at that same library, but this time you have the exact ISBN number of a specific book. You don't need to search through shelves; you go straight to that unique identifier to pull out precisely one book. That's the essence of the get() method.

The get() method is designed to retrieve a single record based on a unique identifier. Most commonly, this is the sys_id (the unique 32-character ID for every record in ServiceNow), but it can also be a unique key-value pair, like an incident number or a user ID. Crucially, because it targets one specific record, it does not require a while(gr.next()) loop.

Key takeaway: No next() needed for get(). If get() finds a record, that record's data is immediately available on the GlideRecord object. If it doesn't find a record, the GlideRecord object will be empty.

Workflow for get():

  1. Initialize GlideRecord: Create a new GlideRecord object for your target table.
  2. Retrieve Record: Call get() with the unique identifier or key-value pair.
  3. Access Data: Directly access fields on the GlideRecord object.
  4. Validate (Recommended!): Use isValidRecord() to confirm a record was actually found.

Practical Examples with get():

1. Retrieving by sys_id

This is the most common and robust way to use get().

var incidentSysId = 'a297e5b52f20011019cfd91df699b68e'; // Replace with a valid sys_id from your instance
var inc = new GlideRecord('incident');
if (inc.get(incidentSysId)) { // If a record with this sys_id is found
    gs.print('Found Incident: ' + inc.number + ' - ' + inc.short_description);
} else {
    gs.print('Incident with sys_id ' + incidentSysId + ' not found.');
}
// Result: Prints the incident number and short description for the matching sys_id, or a 'not found' message.
    
2. Retrieving by Key-Value Pair (e.g., Incident Number)

You can also use get('field_name', 'field_value') for other unique fields.

var incidentNumber = 'INC0009005'; // Replace with a valid incident number
var inc = new GlideRecord('incident');
if (inc.get('number', incidentNumber)) { // Search for 'number' field matching 'INC0009005'
    gs.print('Incident SYS_ID for ' + incidentNumber + ': ' + inc.sys_id);
    gs.print('State: ' + inc.state.getDisplayValue()); // Get display value of state
} else {
    gs.print('Incident ' + incidentNumber + ' not found.');
}
// Result: Prints the sys_id and display state of the incident, or a 'not found' message.
    
3. Combining with isValidRecord() for Robustness

It's good practice to check if get() actually found something.

var incidentNumber = 'INC0010012'; // A number that might or might not exist
var inc = new GlideRecord('incident');
inc.get('number', incidentNumber); // Attempt to retrieve

if (inc.isValidRecord()) { // Check if a valid record was returned
    gs.print(inc.number + ' exists: ' + inc.isValidRecord() + ', Short Description: ' + inc.short_description);
} else {
    gs.print('Incident ' + incidentNumber + ' not found or is invalid.');
}
// Result: Confirms existence and prints details, or indicates if not found.
    

When to Use get():

  • You need to retrieve a single, specific record.
  • You have a unique identifier (like sys_id, number, or other unique keys).
  • You don't need to iterate through a result set.

Key Differences at a Glance

To solidify your understanding, here's a quick comparison:

Featurequery() Methodget() Method
PurposeRetrieve zero, one, or multiple records based on conditions.Retrieve a single, specific record based on a unique identifier.
ReturnsA GlideRecord object that acts as a cursor for a potential set of records.A GlideRecord object representing the found record, or an empty object if not found.
IterationRequires while(gr.next()) to process each record.Does NOT require while(gr.next()); record data is directly available.
ParametersTakes no parameters itself; conditions are added via addQuery(), addEncodedQuery(), etc.Typically takes 1 parameter (sys_id) or 2 parameters ('field_name', 'value').
PerformanceCan be slower if fetching many records or without proper filters.Generally faster for single record retrieval due to direct lookup.
Use CasesReporting, list processing, bulk updates, finding all related items.Opening a specific record, validating existence, retrieving details of a known record.

Common GlideRecord Utilities (Often Used with query() or get() Results)

Regardless of whether you used query() or get(), once you have a GlideRecord object representing a record (or the current record in a loop), you'll often use these methods:

  • getTableName(): Identifies the table the GlideRecord object is associated with.
    var gr = new GlideRecord('change_request');
    gs.print('Table Name: ' + gr.getTableName()); // Result: change_request
                
  • getValue('field_name'): Retrieves the actual, backend value of a field.
    var inc = new GlideRecord('incident');
    inc.get('number', 'INC0000001'); // Assuming it exists
    gs.print('State Value: ' + inc.getValue('state')); // e.g., '1' (for New)
                
  • getDisplayValue('field_name') (or gr.field_name.getDisplayValue()): Retrieves the user-friendly, displayed value of a field (especially useful for reference fields, choice lists).
    var inc = new GlideRecord('incident');
    inc.get('number', 'INC0000001'); // Assuming it exists
    gs.print('State Display Value: ' + inc.state.getDisplayValue()); // e.g., 'New'
                
  • getRowCount(): After a query(), tells you how many records were found. (Not applicable to get().)
    var users = new GlideRecord('sys_user');
    users.addActiveQuery();
    users.query();
    gs.print('Active users found: ' + users.getRowCount());
                
  • hasNext(): Returns true if there are more records in the result set to iterate through (used implicitly by while(gr.next())).
    var inc = new GlideRecord('incident');
    inc.setLimit(1); // Get just one
    inc.query();
    gs.print('Has next? ' + inc.hasNext()); // Will print 'true' if there's at least one record
                
  • getEncodedQuery(): Returns the actual encoded query string that was built. Useful for debugging!
    var inc = new GlideRecord('incident');
    inc.addQuery('active', true);
    inc.addQuery('category', 'software');
    gs.print('Encoded Query: ' + inc.getEncodedQuery()); // Result: active=true^category=software
                
  • getUniqueValue(): Retrieves the unique key of the current record (usually its sys_id).
    var inc = new GlideRecord('incident');
    inc.query();
    if (inc.next()) {
        gs.print('Unique Value (sys_id): ' + inc.getUniqueValue());
    }
                
  • isValid(): Checks if the GlideRecord object is valid and points to a real table.
    var grIncident = new GlideRecord('incident');
    gs.print('Is "incident" table valid? ' + grIncident.isValid()); // Result: true
    var grInvalid = new GlideRecord('non_existent_table');
    gs.print('Is "non_existent_table" valid? ' + grInvalid.isValid()); // Result: false
                
  • isValidField('field_name'): Checks if a specific field exists in the GlideRecord's table.
    var grIncident = new GlideRecord('incident');
    gs.print('Does "incident" table have "category" field? ' + grIncident.isValidField('category')); // Result: true
    gs.print('Does "incident" table have "bogus_field" field? ' + grIncident.isValidField('bogus_field')); // Result: false
                
  • getLink(false) with gs.getProperty('glide.servlet.uri'): Constructs a direct URL to the current record.
    var inc = new GlideRecord('incident');
    inc.setLimit(1);
    inc.query();
    if (inc.next()) {
        gs.print('Link to incident: ' + gs.getProperty('glide.servlet.uri') + inc.getLink(false));
    }
                

Troubleshooting Common Pitfalls

Even seasoned developers trip up sometimes. Here are some common issues and how to avoid them:

  • Forgetting while(gr.next()) after query(): This is probably the most frequent mistake. Without it, your script won't iterate through the results, and you'll often only get the "first" record's data if you access it directly after query() (which isn't reliable).

    Fix: Always wrap your record processing in while(gr.next()) { ... } for query().
  • Using while(gr.next()) with get(): The get() method retrieves a single record directly; it doesn't set up an iterator. Using next() will lead to unexpected behavior or errors.

    Fix: Remove the loop; access fields directly after get(), optionally using if (gr.isValidRecord()) for checks.
  • Incorrect Field Names: Typos in addQuery(), get() parameters, or when accessing gr.field_name can lead to invalid queries or empty results.

    Fix: Double-check field names against the table schema. Use isValidField() for debugging.
  • Missing query() call: You've added all your addQuery() statements, but forgot to actually execute the query!

    Fix: Ensure you call gr.query() after defining your conditions.
  • Unscoped query() on large tables: Running gr.query() without any addQuery() filters on a table with thousands or millions of records can bring your instance to a crawl.

    Fix: Always add conditions with addQuery() or addEncodedQuery(). Use setLimit() if you only need a few samples.
  • Case Sensitivity with Operators: Some string operators like 'CONTAINS' or 'STARTSWITH' should be in uppercase when passed as parameters in addQuery('field', 'OPERATOR', 'value').

    Fix: Use uppercase for string operators.
  • Not checking isValidRecord(): If your get() call doesn't find a record, subsequent attempts to access gr.field_name might result in null or undefined errors.

    Fix: Always use if (gr.isValidRecord()) or if (gr.get(sys_id)) to confirm the record was found.

Interview Relevance: Acing Your ServiceNow Technical Interview

Understanding the difference between query() and get() is a fundamental skill that every ServiceNow developer should possess. It's a common topic in technical interviews, and demonstrating a clear grasp of their distinct uses showcases your foundational knowledge of GlideRecord and efficient scripting practices.

Typical Interview Questions:

  • "Explain the difference between GlideRecord.query() and GlideRecord.get()."
  • "When would you use query() versus get() in a script?"
  • "Can you give an example of how you'd use get() to retrieve an Incident record by its number?"
  • "What happens if you use while(gr.next()) after a gr.get() call?"
  • "How would you ensure a get() call actually retrieved a record before proceeding?" (Answer: isValidRecord() or check the return value of get() in an if condition.)
  • "What are the performance implications of using query() without any filters?"

By articulating the distinct purposes, iteration requirements, and performance considerations, you'll impress your interviewer and demonstrate your capability as a well-rounded ServiceNow developer.

Best Practices for Robust GlideRecord Scripting

Beyond just knowing the methods, here are some principles to guide your GlideRecord journey:

  1. Always Scope Your Queries: Never run an unfiltered query() on a production instance unless you absolutely know what you're doing and the table is tiny. Use addQuery() or addEncodedQuery().
  2. Validate get() Results: Always check if get() successfully retrieved a record using if (gr.isValidRecord()) or by checking the return value of get().
  3. Handle Empty Results Gracefully: Your while(gr.next()) loop won't run if no records are found by query(). Consider what your script should do in that scenario.
  4. Be Specific with Fields: When fetching data, only retrieve the fields you actually need. While GlideRecord is optimized, requesting data for hundreds of fields when you only need one can still add overhead.
  5. Use gs.info() or gs.debug() for Logging: Avoid gs.print() in production scripts; gs.info() and gs.debug() offer better control and integration with system logs.
  6. Test in Non-Production: This cannot be stressed enough. A sandbox instance is your best friend.
  7. Comment Your Code: Explain your logic, especially complex queries or business rules. Future you (or your colleagues) will thank you.

Conclusion

The query() and get() methods are cornerstones of server-side scripting in ServiceNow. While both are used for reading data, they cater to fundamentally different needs: query() for sets of records and get() for individual, specific records. By understanding their distinct mechanics, use cases, and best practices, you're not just writing code; you're writing efficient, robust, and scalable solutions that truly leverage the power of the ServiceNow platform. Keep practicing, keep learning, and happy scripting!


Scroll to Top