How to Use addQuery in ServiceNow: Your Essential Guide to Database Interaction
By a fellow ServiceNow Enthusiast
Alright team, let’s talk ServiceNow. If you’ve spent any time customizing or building solutions on the platform, you’ve undoubtedly encountered the need to interact with data. Fetching records, filtering lists, or updating information – these are daily tasks for any developer or administrator. And when it comes to server-side scripting, there’s one API that stands tall above the rest: GlideRecord. Specifically, mastering its querying capabilities, especially the versatile addQuery method, is absolutely non-negotiable for anyone looking to truly unlock the power of ServiceNow.
Think of it this way: ServiceNow stores all its information in a sophisticated database. While you can see this data through lists and forms, sometimes you need to dig deeper, pull specific sets of information based on complex criteria, or perform operations programmatically. That’s where scripting comes in, and that’s where GlideRecord shines. It’s your direct line to the database, but without having to write a single line of raw SQL. How cool is that?
In this comprehensive guide, we’re going to embark on a journey to demystify GlideRecord, with a sharp focus on its querying methods. We’ll explore the incredibly powerful addQuery, delve into its various forms, and cover other essential methods that let you manipulate and retrieve data like a seasoned pro. So, grab your favorite beverage, clear your mind, and let’s get ready to become GlideRecord gurus!
Unpacking Glide APIs: Your Toolkit for ServiceNow Customization
Before we dive deep into addQuery, let’s set the stage with a quick overview of Glide APIs. ServiceNow isn’t just a platform; it’s a development ecosystem, and Glide APIs are the programmatic interfaces that allow us to bend its will to our specific needs. They provide structured ways to interact with almost every aspect of the platform through scripting, letting us go beyond out-of-the-box functionality.
You’ll typically encounter two main flavors of Glide APIs:
- Client-Side APIs: These run in the user’s browser. Think of tools like
GlideForm(g_form) for manipulating form fields,GlideUser(g_user) for getting user information, orGlideAjaxfor asynchronous server communication. They’re all about enhancing the user experience directly on a form or portal. - Server-Side APIs: These execute on the ServiceNow server. This is where the heavy lifting happens – interacting with the database, performing complex calculations, or integrating with external systems. And guess what? Our star, GlideRecord, lives firmly in this server-side realm.
Other notable server-side APIs include GlideSystem (gs) for logging and utility functions, GlideDate and GlideDateTime for handling dates and times, and GlideAggregation for summarizing data. Each plays a crucial role, but when it comes to direct database manipulation, GlideRecord is your go-to.
What Makes GlideRecord So Special?
At its core, GlideRecord is a special JavaScript class (though it often feels like a Java class under the hood) that operates exclusively on the server. Its primary mission in life is to help you perform CRUD operations – Create, Read, Update, and Delete – on records stored in your ServiceNow instance’s database.
Why can’t we just write SQL queries directly? Well, ServiceNow is designed with abstraction in mind. It uses a robust object-relational mapping (ORM) layer. Instead of worrying about table joins, specific database syntax, or injection vulnerabilities, you interact with records as objects. GlideRecord takes your high-level instructions and translates them into efficient, secure SQL queries behind the scenes. This makes your code cleaner, more portable, and significantly safer.
Here’s a quick rundown of why GlideRecord is such a foundational component:
- The Most Common API: You’ll see it everywhere – Business Rules, Script Includes, Workflows, Fix Scripts, UI Actions, you name it.
- Server-Side Execution: It runs directly on the ServiceNow server, giving it full access to the database.
- SQL Generation: It’s your bridge to the database, converting your script commands into optimized SQL.
- CRUD Operations Made Easy: Creating, reading, updating, and deleting records becomes a breeze, all within the JavaScript environment.
A Crucial Warning from a Fellow Developer:
Always, I repeat, ALWAYS test your GlideRecord queries on a non-production instance first! An incorrectly constructed query – perhaps with a typo in a field name or an invalid condition – can lead to unexpected and potentially catastrophic data loss when performing insert(), update(), or deleteRecord() operations. Think before you commit, and validate your results rigorously in a safe environment.
Before We Query: Setting Up Your Scripting Environment
So, where do you actually write and test these GlideRecord scripts? For quick tests, ad-hoc data manipulation, and learning, your best friend is the “Scripts – Background” module (usually found under System Definition > Scripts – Background). It provides a simple editor where you can run server-side JavaScript and see the output directly.
When you’re running scripts, you’ll want to see what’s happening. That’s where gs.print() and gs.info() come in:
gs.print('Your message here');: This will output your message directly to the “Scripts – Background” output window. It’s great for debugging simple values.gs.info('Your message here');: Similar togs.print(), but it logs the message as an “Info” message in the System Log, which is helpful when your script runs in other contexts like Business Rules or Scheduled Jobs.
Let’s warm up with a couple of basic examples:
Example 1: Hello ServiceNow!
gs.print('Welcome to ServiceNow Academy!');
gs.info('Welcome to ServiceNow Academy!');Result: Both lines will output “Welcome to ServiceNow Academy!” in your script execution window and system logs, respectively.
Example 2: Simple Arithmetic
var a = 10;
var b = 20;
var c = a + b;
gs.print(c); // Or gs.print(a + b);Result: 30
The Heart of the Matter: Mastering addQuery() and Beyond
Now, for the main event! The real power of GlideRecord comes from its ability to retrieve exactly the data you need. This section will guide you through the core querying methods, focusing heavily on addQuery().
Step 1: Instantiating GlideRecord and Executing a Basic Query
Every GlideRecord operation starts with creating an instance of GlideRecord for a specific table. Then, you execute the query and iterate through the results.
// 1. Create a new GlideRecord object for the 'incident' table
var inc = new GlideRecord('incident');
// 2. Execute the query. Without any addQuery() calls, this fetches ALL records.
inc.query();
// 3. Iterate through the results using a while loop and inc.next()
while (inc.next()) {
// Inside this loop, 'inc' represents the current record
gs.print(inc.number); // Print the incident number
// You can access any field on the current record like: inc.short_description, inc.caller_id.name, etc.
}Result: This script will print the number of every single incident record in your instance. (Be careful if you have a lot of incidents!)
Human-like Analogy: Think of new GlideRecord('table_name') as opening a specific filing cabinet. inc.query() is like pulling out all the folders. And while(inc.next()) is like going through each folder, one by one, to read its contents.
Step 2: Introducing addQuery() – Your First Filter
Fetching *all* records is rarely what you want. You need to filter! This is where addQuery() becomes indispensable. It allows you to specify conditions that records must meet to be included in your result set. The simplest form takes two arguments: the field name and the value you want to match.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1'); // Find incidents where priority is '1' (Critical)
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - Priority: ' + inc.priority.getDisplayValue());
}Result: This will print the numbers and display values for priority of all incidents marked as ‘1 – Critical’. Notice I used inc.priority.getDisplayValue() for a more readable output. We’ll cover getValue() vs. getDisplayValue() soon!
Practical Tip: When using the two-parameter addQuery(fieldName, value), GlideRecord assumes an “equals” operator (=). So, inc.addQuery('priority', '1') is shorthand for inc.addQuery('priority', '=', '1').
Step 3: Layering Filters – Multiple addQuery() Calls
What if you need more than one condition? Just call addQuery() multiple times! When you chain multiple addQuery() calls, GlideRecord intelligently combines them using an “AND” operator. This means a record must satisfy ALL the specified conditions to be returned.
var inc = new GlideRecord('incident');
inc.addQuery('active', true); // Condition 1: Incident must be active
inc.addQuery('priority', '1'); // Condition 2: Priority must be '1' (Critical)
inc.addQuery('category', 'software'); // Condition 3: Category must be 'software'
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - ' + inc.short_description);
}Result: This will print the numbers and short descriptions of all active, critical incidents categorized as ‘Software’.
Real-World Scenario: Imagine needing to find all active “High” priority incidents related to “Network” issues that were created today. You’d simply chain four addQuery() calls (active, priority, category, and created_on). This is a very common and powerful pattern.
Step 4: The Power of Operators – addQuery(fieldName, operator, value)
Sometimes, “equals” isn’t enough. You might need “greater than,” “starts with,” or “contains.” This is where the three-parameter version of addQuery() comes into play, allowing you to specify an operator.
Here are some commonly used operators:
- Comparison Operators:
=,!=(not equals),>(greater than),>=(greater than or equals),<(less than),<=(less than or equals). - String Operators (case-insensitive for most):
IN: Matches any value in a list (e.g.,['software', 'hardware']).NOT IN: Does not match any value in a list.STARTSWITH: Field value begins with the specified string.ENDSWITH: Field value ends with the specified string.CONTAINS: Field value includes the specified string anywhere.DOES NOT CONTAIN: Field value does not include the specified string.INSTANCEOF: Checks if a record is an instance of a specific table type (e.g., if a task record is an incident).
Let’s look at some examples:
Example 1: Using Less Than or Equal To (`<=`) and CONTAINS
var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Shorthand for active=true, we'll cover this soon!
inc.addQuery('priority', '<=', '2'); // Priority is Critical (1) or High (2)
inc.addQuery('short_description', 'CONTAINS', 'SAP'); // Short description contains "SAP"
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - ' + inc.short_description);
}Result: Prints incidents that are active, have a priority of 1 or 2, and whose short description contains "SAP".
Example 2: Using the `IN` Operator
var categories = ['software', 'hardware'];
var inc = new GlideRecord('incident');
inc.addQuery('category', 'IN', categories); // Category is either 'software' OR 'hardware'
inc.query();
while (inc.next()) {
gs.print(inc.getValue('number') + ' - Category: ' + inc.getValue('category'));
}Result: Prints incidents where the category is 'software' or 'hardware'. Notice how the IN operator effectively acts as an "OR" for a single field's values.
Example 3: Using `STARTSWITH`
var inc = new GlideRecord('incident');
inc.addQuery('category', 'STARTSWITH', 'net'); // Category starts with 'net' (e.g., 'network')
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - Category: ' + inc.category);
}Result: Prints incidents where the category starts with "net".
Interview Relevance: A common interview question is to ask the difference between `CONTAINS` and `STARTSWITH`. Remember: `STARTSWITH` is usually more performant as it can often utilize database indexes more effectively. Use `CONTAINS` only when you genuinely need to search for a substring anywhere in the field.
Step 5: addEncodedQuery() - The Shortcut for Complex Queries
While chaining addQuery() calls is great for simple "AND" conditions, what if your query gets really complex, with multiple "OR" conditions or nested logic? That's where addEncodedQuery() swoops in to save the day. An encoded query is a string representation of a query, similar to what you see in the URL when you apply filters in a list view.
The best part? You don't have to manually construct these complex strings!
How to Get an Encoded Query String:
- Navigate to any list view in ServiceNow (e.g., Incident > All).
- Apply your desired filters using the condition builder. Include "AND" and "OR" conditions as needed.
- Once your conditions are set, click "Run".
- Right-click on the query breadcrumb (the series of filters at the top of the list) and select "Copy query". This will copy the encoded query string to your clipboard.
Example 1: Using a Direct Encoded Query String
var inc = new GlideRecord('incident');
// Example Encoded Query: active=true AND (category=software OR category=hardware) AND priority=1
inc.addEncodedQuery('active=true^category=software^ORcategory=hardware^priority=1');
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - Category: ' + inc.category + ' - Priority: ' + inc.priority.getDisplayValue());
}Example 2: Using a Variable for the Encoded Query
var myEncodedQuery = 'active=true^category=software^priority=1'; // You could get this from 'Copy Query'
var inc = new GlideRecord('incident');
inc.addEncodedQuery(myEncodedQuery);
inc.query();
while (inc.next()) {
gs.print(inc.number);
}Result: Both examples print incidents matching the specified encoded query. The second method is particularly useful if you need to dynamically build parts of your query or store it for reuse.
Troubleshooting Encoded Queries: Be extremely careful with encoded queries. A single typo in a field name, operator, or value can render the entire query invalid, potentially leading to incorrect results or even data loss if used in an update/delete operation. Always test thoroughly!
Step 6: Specialized Query Methods - Quick Wins for Common Scenarios
ServiceNow offers some handy shortcuts for frequently used query conditions:
`addActiveQuery()` and `addInactiveQuery()`
These are super convenient. Instead of typing inc.addQuery('active', true) or inc.addQuery('active', false), you can simply use these methods.
// Fetch active, critical incidents
var incActive = new GlideRecord('incident');
incActive.addActiveQuery(); // Equivalent to incActive.addQuery('active', true);
incActive.addQuery('priority', '1');
incActive.query();
while (incActive.next()) {
gs.info('Active Critical Incident: ' + incActive.number);
}
// Fetch inactive, critical incidents
var incInactive = new GlideRecord('incident');
incInactive.addInactiveQuery(); // Equivalent to incInactive.addQuery('active', false);
incInactive.addQuery('priority', '1');
incInactive.query();
while (incInactive.next()) {
gs.info('Inactive Critical Incident: ' + incInactive.number);
}`addNullQuery()` and `addNotNullQuery()`
These methods are perfect for finding records where a specific field's value is either empty (null) or not empty (not null).
// Find incidents with an empty short description (which should ideally not happen!)
var incNull = new GlideRecord('incident');
incNull.addNullQuery('short_description');
incNull.query();
while (incNull.next()) {
gs.print('Incident with null short description: ' + incNull.number);
}
// Find incidents where the short description is NOT empty
var incNotNull = new GlideRecord('incident');
incNotNull.addNotNullQuery('short_description');
incNotNull.query();
while (incNotNull.next()) {
gs.print('Incident with non-null short description: ' + incNotNull.number);
}Beyond Filtering: Controlling Your Query Results
Once you've filtered your records, you often need to control how they are presented or how many are retrieved. GlideRecord offers several methods for this.
`orderBy()` and `orderByDesc()`: Sorting Your Data
These methods allow you to sort your query results in ascending (orderBy()) or descending (orderByDesc()) order based on a specified field. You can call them multiple times for multi-level sorting.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.addQuery('category', 'software');
inc.orderBy('short_description'); // Sort by short description in ascending order
// inc.orderByDesc('short_description'); // For descending order
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - ' + inc.short_description);
}Result: Prints critical software incidents, sorted alphabetically by their short description.
`setLimit()`: Limiting Your Records
For performance and practicality, you often don't need to retrieve thousands of records. setLimit() allows you to specify the maximum number of records to return.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.orderByDesc('sys_created_on'); // Get the newest ones first
inc.setLimit(10); // Only retrieve the first 10 records
inc.query();
while (inc.next()) {
gs.print('Latest Critical Incident: ' + inc.number + ' - ' + inc.short_description);
}Result: Prints the 10 most recently created critical incidents.
Performance Tip: Always use setLimit() if you know you only need a subset of records. Retrieving unnecessary data is a common performance killer in ServiceNow scripting.
`get()`: Fetching a Single Record
If you know the unique identifier (like sys_id) or another unique field (like number for an incident), you can use get() to fetch a single record directly, often bypassing the need for query() and next() for that specific record.
// Fetch by sys_id
var sysId = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6'; // Replace with an actual sys_id
var inc1 = new GlideRecord('incident');
if (inc1.get(sysId)) { // get() returns true if a record is found
gs.print('Found Incident by sys_id: ' + inc1.number);
} else {
gs.print('Incident with sys_id ' + sysId + ' not found.');
}
// Fetch by another unique field (e.g., 'number')
var incNumber = 'INC0009005'; // Replace with an actual incident number
var inc2 = new GlideRecord('incident');
if (inc2.get('number', incNumber)) { // get(fieldName, value)
gs.print('Found Incident by number: ' + inc2.sys_id);
} else {
gs.print('Incident with number ' + incNumber + ' not found.');
}Result: Prints the incident number for the given sys_id, or the sys_id for the given incident number.
Common Mistake: Don't use get() if you're expecting multiple records. get() is for one, specific record. For multiple records, always use query() and while(gr.next()).
`chooseWindow()`: Paginating Your Results (Advanced)
This method allows you to fetch records within a specific range, useful for implementing custom pagination. It includes the first value and excludes the second. So chooseWindow(3,7) would return records at index 3, 4, 5, 6.
var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('priority', '1');
inc.orderBy('number'); // Order is important for consistent windowing
inc.chooseWindow(3, 7); // Get records from the 4th to the 7th (index 3 to 6)
inc.query();
while (inc.next()) {
gs.print('Windowed Incident: ' + inc.number);
}Result: Prints 4 incident numbers (indices 3, 4, 5, 6) from the active, priority 1 incidents, assuming they exist.
`getRowCount()`: Counting Your Blessings (or Records)
Instead of iterating through all records just to count them, getRowCount() returns the total number of records that match your query.
var activeUsers = new GlideRecord('sys_user');
activeUsers.addActiveQuery(); // Find all active users
activeUsers.query();
gs.print('Total Active Users: ' + activeUsers.getRowCount());Result: Prints the total count of active users in the system.
Interview Relevance: When would you use getRowCount() versus iterating with `next()`? If you only need the count, `getRowCount()` is far more efficient as it often avoids fetching all the actual record data. If you need to *do something* with each record, then `next()` is necessary.
`getEncodedQuery()`: Inspecting Your Query
This method is super handy for debugging. After building your query with addQuery(), you can use getEncodedQuery() to see the actual encoded query string that GlideRecord constructed.
var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('priority', '1');
inc.addQuery('category', 'software');
inc.query(); // You still need to call query() for the encoded query to be fully built based on all addQuery calls
gs.print('Encoded Query Used: ' + inc.getEncodedQuery());Result: Will print something like: Encoded Query Used: active=true^priority=1^category=software
Accessing Data & Record Manipulation (Briefly)
While this article focuses on querying, it's important to know how to interact with the data you retrieve.
`getValue()` vs. `getDisplayValue()`: A Crucial Distinction
This is a classic ServiceNow question! They both retrieve a field's value, but differently:
- `getValue('fieldName')`: Returns the actual, stored value in the database. For reference fields, this is the
sys_id. For choice lists, it's the backend integer or string value. - `getDisplayValue('fieldName')`: Returns the user-friendly, displayed value, considering labels, choice values, and referenced record names.
var inc = new GlideRecord('incident');
inc.get('number', 'INC0000001'); // Assuming INC0000001 exists
if (inc.isValidRecord()) {
gs.print('Priority (Value): ' + inc.getValue('priority')); // e.g., '1'
gs.print('Priority (Display Value): ' + inc.getDisplayValue('priority')); // e.g., '1 - Critical'
gs.print('Caller (Value): ' + inc.getValue('caller_id')); // e.g., 'a1b2c3...' (sys_id)
gs.print('Caller (Display Value): ' + inc.getDisplayValue('caller_id')); // e.g., 'Fred Luddy'
}Basic Record CRUD (Create, Update, Delete)
addQuery() is often just the first step. Once you've identified records, you might want to change them.
`initialize()` and `insert()`: Creating New Records
var newInc = new GlideRecord('incident');
newInc.initialize(); // Prepares a new, blank record
newInc.category = 'network';
newInc.short_description = 'Server unreachable';
newInc.priority = 1;
newInc.insert(); // Saves the new record to the database
gs.print('New Incident created: ' + newInc.number);`update()` and `updateMultiple()`: Changing Existing Records
// Update a single record
var incToUpdate = new GlideRecord('incident');
if (incToUpdate.get('number', 'INC0000057')) { // Replace with an actual incident
incToUpdate.state = 2; // Set state to 'In Progress'
incToUpdate.update();
gs.print('Incident ' + incToUpdate.number + ' updated.');
}
// Update multiple records based on a query
var incidentsToBulkUpdate = new GlideRecord('incident');
incidentsToBulkUpdate.addQuery('category', 'hardware');
incidentsToBulkUpdate.addQuery('state', 1); // Only open hardware incidents
incidentsToBulkUpdate.setValue('category', 'software'); // Change category to software
incidentsToBulkUpdate.updateMultiple(); // Updates all matching records
gs.print('Multiple hardware incidents moved to software category.');Note: updateMultiple() is extremely efficient for bulk updates as it sends a single database command. Using `while(gr.next()) { gr.update(); }` for many records is much slower.
`deleteRecord()` and `deleteMultiple()`: Removing Records
// Delete a single record
var incToDelete = new GlideRecord('incident');
if (incToDelete.get('number', 'INC0010013')) { // Replace with an actual incident number
incToDelete.deleteRecord();
gs.print('Incident ' + 'INC0010013' + ' deleted.');
}
// Delete multiple records based on a query
var oldTestIncidents = new GlideRecord('incident');
oldTestIncidents.addQuery('short_description', 'STARTSWITH', 'TEST - OLD');
oldTestIncidents.addQuery('sys_created_on', '<', 'javascript:gs.daysAgo(30)'); // Older than 30 days
oldTestIncidents.deleteMultiple();
gs.print('Multiple old test incidents deleted.');Danger Zone Reminder:
Use deleteRecord() and especially deleteMultiple() with extreme caution and only after rigorous testing in non-production. Deleting data is irreversible!
ACL Checks: `canCreate()`, `canRead()`, `canWrite()`, `canDelete()`
Before performing CRUD operations, it's good practice to check if the current user (or the script's impersonated user) has the necessary permissions based on Access Control Lists (ACLs).
var gr = new GlideRecord('incident');
gs.print('Can create incident: ' + gr.canCreate());
gs.print('Can read incident: ' + gr.canRead());
gs.print('Can write incident: ' + gr.canWrite());
gs.print('Can delete incident: ' + gr.canDelete());Result: Returns `true` or `false` based on the user's roles and ACLs for the incident table.
Advanced Control: `autoSysFields(false)` and `setWorkflow(false)`
These methods offer granular control over how GlideRecord interacts with system fields and business rules.
- `gr.autoSysFields(false)`: Prevents system fields like
sys_updated_by,sys_updated_on,sys_mod_count, etc., from being automatically updated when you callupdate(). Useful for data migrations or specific integrations where you want to preserve original system metadata. - `gr.setWorkflow(false)`: Prevents business rules, workflow engines, and other automatic platform behaviors from running when you call
update()orinsert(). Use with extreme caution, as it can bypass important logic.
var inc = new GlideRecord('incident');
inc.addQuery('state', 1); // Find New incidents
inc.setLimit(5); // Only update a few for testing
inc.query();
while (inc.next()) {
inc.autoSysFields(false); // Do NOT update system fields like updated_on/by
inc.setWorkflow(false); // Do NOT trigger Business Rules/Workflows
inc.state = 2; // Set state to 'In Progress'
inc.update();
gs.print('Updated ' + inc.number + ' without autoSysFields or workflow.');
}Note: `autoSysFields` may not function as expected in scoped applications due to platform architectural differences. Always verify behavior in your specific environment.
`addJoinQuery()`: Bridging Tables (Advanced)
This method allows you to perform a simple join between two tables to filter results. It's an advanced concept but powerful for specific scenarios.
// Find problems that have an associated incident where the opened_by of the problem
// matches the caller_id of the incident.
var prob = new GlideRecord('problem');
prob.addJoinQuery('incident', 'opened_by', 'caller_id');
prob.query();
while (prob.next()) {
gs.print('Problem with associated incident (join): ' + prob.number);
}Result: Prints problem records that have at least one incident record associated with them where the specified fields match.
Troubleshooting Common `addQuery` Issues
Even the pros hit snags. Here are some common issues you might encounter and how to troubleshoot them:
- No Results Returned:
- Incorrect Field Name: Double-check the exact field name (e.g., `short_description` vs. `shortdescription`). Typo is the most common culprit.
- Case Sensitivity: While GlideRecord is generally forgiving, some values or operators might be case-sensitive, especially in custom scripts or integrations.
- Data Type Mismatch: Are you querying a reference field with a display name instead of a `sys_id`? Are you comparing a string to an integer? `addQuery('priority', 1)` usually works for '1', but be explicit if issues arise: `addQuery('priority', '=', '1')`.
- Overly Restrictive Query: Maybe your combined `addQuery()` conditions are too tight, and no records actually meet *all* of them. Use `gs.print(gr.getEncodedQuery())` to see the full query and manually test it in a list view.
- Too Many Results Returned (or Wrong Results):
- Missing `addQuery()`: Forgot to add a crucial filter.
- Misunderstanding `AND`/`OR` Logic: Remember, chained `addQuery()` calls are always `AND`. For `OR` conditions on different fields, you typically need `addEncodedQuery()`.
- `CONTAINS` vs. `STARTSWITH` vs. `=` : Ensure you're using the correct operator for your search intent. `CONTAINS` will find substrings anywhere, which might return more than you want.
- Performance Problems:
- No `setLimit()`: Always use `setLimit()` if you only need a few records.
- Broad `CONTAINS` Queries: Searching for a small substring in large text fields across many records can be very slow.
- Unindexed Fields: Querying frequently on fields that aren't indexed can impact performance. This is more of a platform-level issue but worth noting.
Interview Prep: Questions You Might Hear
Mastering GlideRecord isn't just for coding; it's also a hot topic in ServiceNow interviews. Be prepared for these:
- "What is GlideRecord, and why do we use it instead of direct SQL?" (Focus on abstraction, security, CRUD, server-side.)
- "Explain the difference between
addQuery()andaddEncodedQuery(). When would you use each?" (Simple vs. complex queries, generating encoded queries from list views.) - "When would you use
get()versus a combination ofquery()andnext()?" (Single record by unique key vs. iterating through multiple results.) - "What's the difference between
getValue()andgetDisplayValue()?" (Raw data vs. user-friendly display.) - "How do you ensure good performance when writing GlideRecord queries?" (Use `setLimit()`, avoid broad `CONTAINS`, filter early, use specific `addQuery()` where possible.)
- "What are `autoSysFields(false)` and `setWorkflow(false)` used for, and why should you use them carefully?" (Bypassing system field updates and business rules/workflows, potential for data integrity issues.)
Conclusion: Mastering Your ServiceNow Database Interactions
Phew! That was a deep dive, wasn't it? By now, you should have a solid grasp of GlideRecord's querying capabilities, especially the powerful and versatile addQuery() method. You've learned how to filter records, sort them, limit your results, and even tackle complex queries with addEncodedQuery().
GlideRecord is the backbone of server-side scripting in ServiceNow. Becoming proficient with it will empower you to build robust, efficient, and intelligent solutions that truly leverage the platform's data. Remember the golden rules: test in non-production, filter early and efficiently, and always be mindful of performance and data integrity.
The best way to truly master these concepts is to practice, practice, practice! Open up your personal developer instance, head over to "Scripts - Background," and start experimenting. Build queries, iterate through results, try different operators, and observe the output. The more you play, the more intuitive it will become.
Happy scripting, and may your GlideRecords always return exactly what you're looking for!