sabell2012
Mega Sage
Mega Sage

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

 

sabell2012_0-1697143950182.png

 

Click on the Try It link.

 

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

 

sabell2012_1-1697144112452.png

 

Your results should look similar to this:

 

sabell2012_2-1697144282623.png

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:

 

sabell2012_3-1697144455082.png

 

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.

 

sabell2012_4-1697144716185.png

 

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

 

sabell2012_5-1697145241815.png

 

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

 

sabell2012_6-1697145562206.png

 

At which point you have all the columns available.

 

sabell2012_7-1697145618671.png

 

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!

 

sabell2012_0-1697214757739.png

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

 

sabell2012_1-1697215053725.png

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.

 

sabell2012_2-1697224183564.png

 

After arranging what fields are displayed:

sabell2012_1-1697224148465.png

 

Your results should look something like this:

 

sabell2012_0-1697224108225.png

 

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:
 

9. Union of Two Tables Diagram.jpg

 

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

 

2. 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:

 

sabell2012_0-1697225903771.png

 

Some Final Thoughts

 

 

Enjoy!

Steven Bell.

 

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

 

sabell2012_1-1696102033098.png


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.

14 Comments