Tips and Tricks for Developer Forms Hierarchical Tree Tools
Jennifer Croy
Rumken, Inc.
Rumpi Gravenstein
Rumken, Inc
Michael Rife
The Goodyear Tire and Rubber Company
Gerhardt Martin
The Goodyear Tire and Rubber Company
Abstract
The Oracle-provided hierarchical tree display tools can be quite powerful. However, the NavWiz demonstration form and the succeeding Forms 6.0 hierarchical tree object have inherent deficiencies in functionality. Perhaps most importantly, the query method proves too slow for large hierarchies. These hierarchical tree tools can be tuned to dramatically improve their speed, utility, and features. This presentation will focus on describing the methods and techniques used to successfully implement the NavWiz tool in an actual Oracle Web Forms deployment. Techniques that will be discussed include optimization approaches for handling large hierarchies, branch tree selections, and much more.
Topics to Discuss
Primer
on the Oracle Provided Hierarchical Tree Tools
Hierarchical
Query Optimizations
Supporting Multiple Tree Objects
Definition of Hierarchical Query
A hierarchical query is a SQL query that shows parent child relationships between records that are often in the same table. Almost all data has a natural hierarchical structure which can be used to present data in a more meaningful way. An example is the employee hierarchy within an organization where you can determine, just by looking at the hierarchy, a number of important attributes like the number of departments and the size of a department. Another reason that data is easier to understand when displayed in a hierarchical structure is that it becomes much easier to search. For instance, if you wanted to find the secretary or treasurer of a company it is easier to look through an organizational chart than having to scan each name and the associated job title. For these reasons I have been using hierarchical displays in an increasing number of applications.
Consider the EMP demonstration table provided by Oracle.
EMP
-------------
EMPNO
Employee Number
ENAME Employee Name
JOB Employee job title
MGR
Employee number of this employee's manager
In this table the MGR column refers to the EMPNO
column in another row in the same table.
All but one employee, the President, has a manager whose employee record
can also be found in the EMP. To make
this type of search easy, Oracle has provided support for what is referred to
as a hierarchical query. The SQL
constructs that support this type of query are START WITH and CONNECT BY PRIOR. Start with is used to identify where in the
hierarchical tree the query should start while connect by describes how one
record relates to the next. In the case
of the emp table the following query can be used to display the entire
hierarchy. In this query it is assumed
that the President of the company will not have a manager and therefore if the
query is to display the entire organizational chart it needs to start where the
mgr field is either NULL or the JOB is PRESIDENT. I have added some spacing and the pseudo column, Level, to
illustrate this tree in the following SQL.
select lpad(' ',3*(level-1))||ename name, job,
mgr, empno, level
from emp
start
with mgr is null
connect
by prior empno = mgr
NAME JOB
MGR EMPNO LEVEL
------------------------ --------- --------
---------- ----------
KING PRESIDENT 7839
1
JONES
MANAGER 7839 7566 2
SCOTT ANALYST 7566 7788 3
ADAMS CLERK 7788 7876 4
FORD ANALYST 7566 7902 3
SMITH CLERK 7902 7369 4
BLAKE MANAGER 7839 7698 2
ALLEN SALESMAN 7698 7499 3
WARD SALESMAN 7698 7521 3
MARTIN SALESMAN 7698 7654 3
TURNER SALESMAN 7698 7844 3
JAMES CLERK 7698 7900 3
CLARK
MANAGER 7839 7782 2
MILLER CLERK 7782 7934 3
14 rows selected.
The Level pseudo column shows the depth at which
the row is in the hierarchy. Thus the
president is at Level 1 while those that report to him are at level 2 and so
on. If you are interested in just the
part of the tree that includes Blake and all of the people that report to him,
the query would be
select lpad(' ',3*(level-1))||ename name, job,
mgr, empno
from emp
start
with ename = 'BLAKE'
connect
by prior empno = mgr
NAME JOB MGR EMPNO
------------------------------ ---------
---------- ----------
BLAKE MANAGER 7839 7698
ALLEN
SALESMAN 7698 7499
WARD
SALESMAN 7698 7521
MARTIN SALESMAN 7698 7654
TURNER
SALESMAN 7698 7844
JAMES
CLERK 7698 7900
6 rows selected.
Information to be Aware of When Using
Hierarchical Queries
There are a couple of caveats that need to be
pointed out. First, in a hierarchical
query, avoid use of the WHERE clause.
The WHERE clause is evaluated after the hierarchy has been created. Therefore, evaluation of the WHERE clause
may create “gaps” in the hierarchy.
When displaying the hierarchy tree via the tree tools, the results may
not be correct if gaps exist. For
instance, in the following example, JONES has two people reporting directly to
him: SCOTT and FORD. JONES is being
excluded from the hierarchy. However,
the tree tool displays this query as FORD reporting to SCOTT, rather than
showing FORD and SCOTT at the same level.
select lpad(' ',3*(level-1))||ename name, job,
mgr, empno
from emp
where
ename != ‘JONES’
start
with ename = ‘JONES’
connect
by prior empno = mgr
NAME JOB MGR EMPNO
--------------------
--------- ---------- ----------
SCOTT ANALYST
7566 7788
ADAMS CLERK
7788 7876
FORD ANALYST
7566 7902
SMITH CLERK
7902 7369

Instead, one should use the CONNECT BY and START
WITH statements to include any needed row restrictions. This will mean that if a node is excluded,
its entire subtree will also be excluded.
Second, in a hierarchical query, if you use the
ORDER BY clause, the rows are not ordered by the hierarchy tree but rather by
the ORDER BY columns. The following SQL
demonstrates this issue:
select lpad(' ',3*(level-1))||ename name, job,
mgr, empno, level
from emp
start
with mgr is null
connect
by prior empno = mgr
order by
name
NAME JOB
MGR EMPNO LEVEL
------------------------ --------- -------
---------- ----------
ADAMS CLERK
7788 7876 4
SMITH CLERK 7902 7369 4
ALLEN SALESMAN 7698 7499 3
FORD ANALYST 7566 7902 3
JAMES CLERK 7698 7900 3
MARTIN SALESMAN 7698 7654 3
MILLER CLERK 7782 7934 3
SCOTT ANALYST 7566 7788 3
TURNER SALESMAN 7698 7844 3
WARD SALESMAN 7698 7521 3
BLAKE
MANAGER 7839 7698 2
CLARK
MANAGER 7839 7782
2
JONES
MANAGER 7839 7566 2
KING PRESIDENT 7839
1
14 rows selected.
The net affect of this behavior is that there is
no easy way in which to order the employees that report to BLAKE, or even to
ensure that they will always appear in the same order.
There are some tricks to affect the order in
which the records are displayed. One
method is discussed later in
this paper. Another
way is to create an index that will affect the order in which the records are
retrieved from the database.
Hierarchical Tree Tools
A hierarchical tree tool displays a hierarchical query as a navigator-style window, similar to NT Explorer (see Figure 1). You can click on any individual line to display or hide the corresponding data.

Figure 1
Developer Forms 5.0 contained a demonstration form, NavWiz.fmb, and a forms
library, Navigator.pll, that displayed a hierarchical tree based on the
scott/tiger schema. This demonstration
could be adapted for use in your own application, but was not very sturdy or
particularly easy to use.
Developer Forms 6.0 now has a built-in hierarchical tree object. The built-in object seems significantly sturdier and easier to use. It does retain the manipulation procedures that were found in the Forms 5 demo, allowing for more developer customization and improvement.
Primer on the Oracle Provided Hierarchical Tree Tools
Forms 5.0 NavWiz Demo Tool
Developer Forms 5.0 had a demonstration hierarchical tree tool. This method used just the normal items readily available within Forms: a multi-record block with a single text item, some control items, a stacked canvas, and a window. You also attached the Navigator.pll library, which provided the functions to manipulate the items.
One primary limitation I saw with this approach was that the names of the items were hard-wired into the Navigator library. This not only meant that you were required to use the exact names (“NAVIGATOR”, “NAVIGATOR_CONTROL”, etc.) it was expecting, but also meant that you could not have more than one hierarchical tree per form. I later adapted the Navigator library to allow multiple trees within a single form.
The Forms 5.0 demo tool supported features such as multi-select and range-select using the CTRL and SHIFT keys, at the developer or user’s discretion. It supported replacing the entire tree or adding and deleting nodes. The elements of the tree were simply recorded in a record group that could be modified.
Forms 6.0 Hierarchical Tree Object
With the release of Developer 6.0, a drop-in hierarchical tree tool was included. To create a tree, create a block item and set the item type to “Hierarchical tree.” You should then add a call to the FTREE.populate_tree Built-in, usually in the WHEN-NEW-FORM-INSTANCE trigger, to populate the tree.
This tool allowed multiple tree objects per form, without any modifications. It also supported the same features found in the Forms 5.0 demo tool, such as multi-select and range-select.
Hierarchical Query Performance
Despite all the features available, neither the Version 5 nor the Version 6 Developer 2000 hierarchical tree tools are perfect “out of the box.” The biggest obstacle to widespread use of the hierarchical tree is the amount of time it can take to display a large tree. This is a problem because, in my experience, hierarchical trees are used primarily when there is a large amount of data to display. Unfortunately, this is just what the Oracle provided tool does poorly. The default hierarchical tree tool behavior is to run the entire query at once, before displaying the tree. When there are thousands of rows this can take a long time, time that is not needed if you are only interested in the first or second level or a particular branch in the tree. Clearly, this isn’t a viable solution for large interactive queries where your form users would not be able to continue until the query finished.
Branch Tree Selections
I also wished to have the option of a branch tree selection, an often requested feature. That is, when a user selects a node for use, that the application would automatically recognize that all the descendants of the “selected” node should also be selected for use. For instance, in an employee hierarchy, an example of a desired functionality might be to click on a manager and select an option to send an email to that person and all people that report underneath him. The Oracle provided tool does not include this feature.
Supporting Multiple Tree Objects
Finally I wished to be able to utilize multiple trees within the same form. The Forms 5.0 demonstration library had apparently not been built with this flexibility in mind. The ability to display multiple tree objects is very desirable in applications that have a number of complicated selections a user must make to define report parameters or enter a complex record.
Hierarchical Query Optimizations
Looking to solve the hierarchical tree performance problem, I have added my own forms library, to supplement the Oracle-provided Forms 5.0 NavWiz demo tool. This library traverses and displays the tree by querying for each hierarchy level individually starting with the main level. Then, as the user drills into a lower level, the application queries data for that level as well. This way, the only information that is accessed is that which the user has actually requested. This is similar to the behavior of NT Explorer.
Taking this approach has a second desired benefit, as well. Since each level is being retrieved individually, the results within a level can be displayed in a given order.
In my supplemental library, I created several support routines, to handle the dynamic creation and expansion of the hierarchical tree.
Support Routines in the supplemental library:
PROCEDURE Add_Smart_Nav_Row(tree varchar2, menu_node_id number);
PROCEDURE Create_Initial_Tree(tree varchar2);
PROCEDURE Create_Smart_Nav(tree varchar2);
FUNCTION Get_Index_Number (tree varchar2, menu_node_id number) RETURN NUMBER;
PROCEDURE Populate_Level (tree varchar2, parent_index number);
PROCEDURE Populate_Tree_Child (tree varchar2, menu_node_id number);
Summary of the Externally Referenced Routines:
At the time of first population (usually occurring in the WHEN-NEW-FORM-INSTANCE trigger), you would call the Create_Initial_Tree routine. This procedure populates the top level of the tree. It then populates one child under each node as a placeholder. Population of at least one child is necessary so that nodes with children are shown as collapsed nodes, rather than being shown as leaf nodes. Another option would be to enter a “bogus” node under every node. This bogus node would then be deleted when that level is populated. This method would likely save some time, since it would not be necessary to retrieve an additional child for every node. It has the disadvantage of showing all nodes as initially being collapsed, even if it is actually a leaf.
The Create_Smart_Nav routine creates a supplemental record group within Forms to keep track of the information for each node. There are three columns in the record group: menu_node_id (the value for the node), all_children_populated_flag (an indicator of whether that node has been expanded), and child_populated_menu_node_id (the value for the populated child node). If a node has no children, it is judged to be a leaf node and the all_children_populated_flag is set to “Y”, to indicate that future population is not necessary. If you use the bogus node method, the child_populated_menu_node_id column would not be needed.
So, at initial population, the supplemental record group for the employee hierarchy would appear as follows:
|
Shown Name |
Menu_node_id |
All_children_populated_flag |
Child_populated_menu_node_id |
Child Name |
|
KING |
7839 |
N |
7566 |
JONES |
|
JONES |
7566 |
N |
0 |
|
When the user expands the KING node, the “expand” trigger will check the value of the KING node’s all_children_populated_flag. Since the value is currently “N”, the remainder of KING’s children will be retrieved, using the Populate_Level procedure. Populate_Level retrieves all children for the given node, retrieves one child for each of these second level nodes, and then sets the all_children_populated_flag.
Now, the supplemental record group will now appear as:
|
Shown Name |
Menu_node_id |
All_children_populated_flag |
Child_populated_menu_node_id |
Child Name |
|
KING |
7839 |
Y |
7566 |
JONES |
|
BLAKE |
7698 |
N |
7521 |
WARD |
|
WARD |
7521 |
N |
0 |
|
|
CLARK |
7782 |
N |
7934 |
MILLER |
|
MILLER |
7934 |
N |
0 |
|
|
JONES |
7566 |
N |
7788 |
SCOTT |
|
SCOTT |
7788 |
N |
0 |
|
In this manner, the next level of the hierarchy is dynamically populated as each node is expanded.
Summary of the Internal Procedures:
Add_Smart_Nav_Row adds a row to the Smart Navigator record group. This is done for every node in the hierarchy. This is called from multiple routines, including Create_Initial_Tree and Populate_Tree_Child.
Create_Smart_Nav deletes and recreates the Smart Navigator record group, which is used for maintenance of the tree. This is called from Create_Initial_Tree.
Get_Index_Number finds the tree index in the Oracle Navigator tree for the given node id. This is used both internally and externally.
Populate_Tree_Child populates a single child for the given parent node, if any children exist. This is called from Populate_Level.
Future Enhancement Ideas
Due to time pressures, I have not yet
upgraded this application to Forms Developer 6.0. However, in the next phase of the project, I intend to convert
the application and take advantage of the built-in hierarchical tree tool.
My supplemental library has thus far
been created specifically for my application, with hard-coded SQL cursors. The potential exists to genericize this
library for use with any SQL query.
However, I found the attempt to be too time-consuming to be worthwhile
at this time. One possible method might
be to utilize DBMS_SQL’s ability to run dynamic SQL statements. It appears this approach would likely require
database procedures as well as the Form libraries, due to the limitations on the
use of DBMS_SQL inside of Forms.
Another often requested feature was branch tree selection. The desired functionality was to easily allow a node and its entire subtree (or descendants) to be selected at once. In my application, I decided not to have the screen highlight every single descendant visually, but rather to do the work behind the scenes. The user would click on a button to indicate that the currently selected node should be used as criteria. (Figure 2.) Upon that click, I would then loop through and execute my “selection” logic for the currently selected node, as well as each node in its subtree. (Figure 3.)

Figure 2

Figure 3
I feel it is best to implement branch tree selection on a case-by-case basis, rather than building this functionality into the default behavior. The desired behavior may not necessarily always be to select the descendants. For instance, in the previously mentioned example of being able to email a manager and all his employees, you might decide to also implement the functionality of allowing the user to email the manager alone, rather than emailing the entire group.
You could approach this in a few ways. Approach A: you could give two different functions, one for emailing the group and one for emailing the person alone. In Approach B, you could choose to give a generic email function and a different function that would allow the user to highlight the entire subtree of the selected node. That way, they would click to highlight the subtree, then click to email all selected nodes. Depending on the situation, either approach could be used.

The “Email this Employee and his Staff” trigger:
DECLARE
CURSOR subtree IS
SELECT empno
FROM emp
CONNECT BY PRIOR empno = mgr
START WITH empno = :employee_hierarchy;
BEGIN
FOR employee IN subtree LOOP
Email(employee.empno);
END LOOP;
END;
Supporting Multiple Tree Objects
Allowing Multiple Tree Items within the Forms 5.0 demo tool
The Forms 5.0 demo library hardcoded the expected names of the tree tool support items. This approach meant that you could not have more than one hierarchical tree object per form. I adapted the Navigator library to allow multiple trees within a single form. This was accomplished by allowing the user to pass in the name of the “tree”. For every subroutine in the Navigator.pll library that accessed a form item, I allowed a tree name to be passed in as an optional parameter. The previously hard-wired “Navigator” was made the default, so that previous references to the Navigator.pll would continue to work without needing any revisions. I based the name of all of the form items on this tree name. For instance, if the tree name was given as “TREE_A”, I would look for the control items in the “TREE_A_CONTROL” block. This limitation could be removed as well, if needed. However, since Developer 6.0 with its built-in hierarchical tree had already been released when I made this modification, I chose not to bury too much time in expanding older technology.
Displaying data in a hierarchical tree fashion can show much information about the structure of the data. However, the default behavior of the Oracle-provided tools is not always optimal. With the addition of some routines, the behavior and functionality can be expanded and customized to the needs of any application.