logo

NJP

ServiceNow Admin 101: Observations on Database Views, Part II

Import · May 03, 2016 · article

NOTE: MY POSTINGS REFLECT MY OWN VIEWS AND DO NOT NECESSARILY REPRESENT THE VIEWS OF MY EMPLOYER, ACCENTURE.

DIFFICULTY LEVEL: INTERMEDIATE
Assumes having taken the class SSNF and has good intermediate level of knowledge and/or familiarity with Scripting in ServiceNow.

In my previous article on Database Views, I shared some basic information on Database Views in ServiceNow. To recap, in a normal report you can only select a single table, but Database Views allow you to pull together data from two or more tables into a single table to report against.

In the second part of this post, I'll present some examples of how to use the basics outlined in the previous post to your advantage.

Additional Notes and Observations

Reference Field using Left Join

Here is an example of how to use the Left Join to create a View that includes the user table but does not constrain on the related approved_by field.

The Database View:

Name: u_left_join_no_constraint

Label: Left Join No Constraint

View Tables:

Table: cmdb_ci (Configuration Item)

Order: 100

Variable Prefix: ci

Where clause: none.

Table: sys_user (User)

Order: 200

Variable Prefix: user

Where clause (I had to do a little research here to find the Assigned To field name to use here):

ci_assigned_to = user_sys_id

Left Join: true

image

Click on the Try It link.

Set up the fields so it looks similar to the screen shot:

image

Your results should look similar to this:

image

Note the results: assigned_to can be blank! This is because we are NOT constraining on the assigned_to field. If you uncheck the Left Join, you will see that those records with an empty assigned_to field are excluded from the View results.

Did you notice the Manager field to the right? That field is coming from the User table. Since this is a Join, all fields available from both tables are included in the view.

Unhiding a Hidden Table

Have you ever noticed that certain tables are not available for reporting? For example, tables like Field Labels and Dictionary. Well, if you wrap them in a View, you can then see them from a report!

As an example: From the report tool you will not be able to find the sys_documentation table:

image

So, let's create a database view that exposes the sys_documentaton table:

Name: u_hidden_tables

Label: Hidden Tables

View Tables:

Table: sys_documentation (Field Labels)

Order: 100

Variable Prefix: field

Where clause: none.

image

Results (after adjusting the columns that will be visible) should look something like this:

image

So, now if we go to the reporting tool what can we see?

image

At which point you have all the columns available.

image

Nothing really to add here. Just a neat trick to make the table available to the reporting application. Cool, huh?

Constraining a View Using a Choice Label

Follow these steps to create a view that constrains on the choice label rather than on the choice value.

The database view:

Name: u_constraining_on_choice_label

Label: Constraining On Choice Label

View Tables:

Table: cmdb_ci (Configuration Item)

Order: 100

Variable Prefix: ci

Where clause: none.

Table: cmdb_ci_computer (Computer)

Order: 200

Variable Prefix: comp

Where clause:

comp_sys_id = ci_sys_id

Table: sys_choice (Choice)

Order: 300

Variable Prefix: choice

Left Join: checked

Where clause:

choice_name = 'cmdb_ci' && choice_element = 'install_status' && choice_label = 'Installed' && choice_value = ci_install_status

NOTE: If && does not work then use AND. It seems a bit random, but one or the other should work. The system is finnicky!

image

After arranging what fields are displayed your results should look something like this:

image

As we want to constrain the View to the Label "Installed" and not the value "1," we have to join in the Choice table, then tell the View where to find the label. Once you do that, tell the View that you want only certain values from the Choice table: the cmdb_ci table (Name field), the Field name (Element). Next, tell it that you want only the label "Installed." Finally, tie it back into the cmdb_ci table by the install_status field value. A lot of stuff!

What would happen if you marked the sys_choice record with Left Join of true? Give it a try and see what happens to your record count!

Two Table Union

Is it possible to join together two unrelated tables? Sort of. This is known as a Union in the database world. The two tables must have a common root table for it to work. Here is an example that has cmdb_ci as that root.

The database view:

Name: u_union_of_two_tables

Label: Union of Two Tables

View Tables:

Table: cmdb_ci (Configuration Item)

Order: 100

Variable Prefix: ci

Where clause: none.

Table: cmdb_ci (Configuration Item)

Order: 200

Variable Prefix: ref

Where clause:

ref_sys_id = ci_sys_id && (ref_sys_class_name = 'cmdb_ci_ip_router' || ref_sys_class_name = 'cmdb_ci_computer')

Table: cmdb_ci_computer (Computer)

Order: 300

Variable Prefix: comp

Where clause:

ci.sys_id = comp.sys_id

Table: cmdb_ci_ip_router (Router)

Order: 400

Variable Prefix: router

Where clause:

ci.sys_id = router.sys_id

NOTE: The where clause order is extremely important. You must have ci.sys_id before the table you want to join:

Give me all ci's where it is a router

Give me all ci's where it is a computer

otherwise it will flip the "ask" to be:

Give me all computers that are a ci (returns nothing!?)

Give me all routers that are a ci (returns just the routers in the final list)

Try it. Sort-of makes sense.

image

After arranging what fields are displayed:

image

Your results should look something like this:

image

With this example you can see how to bring in two tables with the same root (cmdb_ci) and have the unique fields from both present in the View.

I started out with our root: cmdb_ci, then constrained the View to just routers and computers. To bring in fields unique to each table, I tied in the Computer and IP Router tables to cmdb_ci via their respective sys_ids. It can be visualized like this:

image

This is a Union query only in a matter of speaking. The router and computer tables do not share records, but they are related in the hierarchy to cmdb_ci. In the database world, you could bring in two totally unrelated tables, but you won't be able to do that with Database Views.

Some Additional Notes

1. It is possible to embed MySQL functions into the Where Clause, but you aren't guaranteed they will work.

Example: return all records from cmdb_ci where the updated date is less than the current date/time AND the Name field contains the letters 'HP.' Both will be ignored. It just does not see them. I have not done an exhaustive look to see what does and does not work. Maybe in another article.

ci.sys_updated_on < NOW() AND INSTR(ci.name, 'HP') > 0

  1. Use parentheses to separate OR collections from AND statements. This DOES function, and is very useful.

Example:

(ci.name = 'Omniwobble Server' || ci.sys_class_name = 'cmdb_ci_server') && (ci.name = 'Guest Computer' || ci.sys_class_name = 'cmdb_ci_computer')

3. Not all MySQL commands are available for Database Views. For example, I found that the "IN" statement does not appear to work. These actually throw errors.

ci.sys_class_name IN ('Computer', 'Software')

or

FIND_IN_SET(ci.sys_class_name, 'Computer, Software')

4. You will need to get creative to check for a null field. IS NOT NULL does not work.

ci.ram IS NOT NULL

Nor:

ci.ram != NULL

Nor:

ci.ram <> NULL

Oddly this failed:

ci.ram > 0

There was no error returned. Just ignored.

Some Cool Stuff

Querying Database Views from a GlideRecord? All of the View fields need to be underscore-walked if you want to reference them!

Example which I ran from Scripts - Background:

var referenceExample = new GlideRecord('u_union_of_two_tables');
referenceExample.addQuery('ref_sys_class_name', 'cmdb_ci_computer');
referenceExample.addQuery('ref_name', 'JASONHWXP'); // I picked one I knew would return info
referenceExample.addNotNullQuery('comp_ram');
referenceExample.query();

gs.info(referenceExample.getRowCount());

while(referenceExample.next()) {
    gs.info('---> \nName:{0}\nClass:{1}\nRAM:{2}',
        [referenceExample.ref_name,
        referenceExample.ref_sys_class_name,
        referenceExample.comp_ram]);
}

Results should look something like this:

image

Some Final Thoughts

Enjoy!

Steven Bell.

If you find this article helps you, don't forget to log in and mark it as "Helpful"!

image

Originally published on: 05-03-2016 03:58 PM

I updated the code and brought the article into alignment with my new formatting standard.

NOTE: ON APRIL 1, 2016 ACCENTURE COMPLETED THE ACQUISITION PROCESS OF CLOUDSHERPAS. AT THAT TIME THE CLOUDSHERPAS BLOG SITES WERE DOWNED FOREVER.

THIS IS THE RE-PUBLICATION OF MY ARTICLE FROM July 17, 2014 ON THE CLOUDSHERPAS SERVICENOW SCRIPTING 101 BLOG.

View original source

https://www.servicenow.com/community/developer-blog/servicenow-admin-101-observations-on-database-views-part-ii/ba-p/2289605