Blog
Writing Oracle Heirarchical Queries
Oracle provides an easy way to deal with a hierarchy relationship within a database, through a type of query called a hierarchical query. The query allows you to use the hierarchy relationship as the criteria for showing records. Here is a simple version of this query using the EMPLOYEES table from the HR sample schema. Select EMP.LAST_NAME, EMP.FIRST_NAME, EMP.EMPLOYEE_ID, EMP.MANAGER_ID From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID The results from this query are as follows: LAST_NAME FIRST_NAME EMPLOYEE_ID MANAGER_ID Kochhar Neena 101 100 Whalen Jennifer 200 101 Mavris Susan 203 101 Baer Hermann 204 101 Higgins Shelley 205 101 Gietz William 206 205 Greenberg Nancy 108 101 Faviet Daniel 109 108 Chen John 110 108 Sciarra Ismael 111 108 Urman Jose Manuel 112 108 Popp Luis 113 108 In the above query the SELECT and the FROM are the same as any other query. The Hierarchical part begins at the START WITH clause. This clause indicates which record(s) the query will start with. The next part is the CONNECT BY…PRIOR clause, the CONNECT BY clause indicates which columns will be compared to determine the hierarchical relationship. The PRIOR clause is used to determine what column will be used to get the next value. In the above example The START WITH clause tells the query that it should start with any row that has an employee_id of 101. The CONNECT_BY clause tells the query to look for the relationship in columns EMPLOYEE_ID and MANAGER_ID The PRIOR clause tells the query where to get the next value to evaluate; in this case it finds all rows where the MANAGER_ID is 101. It then takes the values from the EMPLOYEE_ID column for all those rows and uses those values to find all rows in the MANAGER_ID column that match the new set of values. This process will continue until it gets to values in the EMPLOYEE_ID column that it cannot find in the MANAGER_ID column. If the CONNECT BY clause read EMP.EMPLOYEE_ID = PRIOR EMP.MANAGER_ID so that the PRIOR is in front of the MANAGER_ID instead of the EMPLOYEE_ID then it would again start with any row that has 101 In the EMPLOYEE_ID column, however instead of going to the EMPLOYEE_ID column to get the next value to evaluate it would use the value from the MANAGER_ID column and find where those values from the MANAGER_ID column existed in EMPLOYEE_ID column. If we change the above query to read CONNECT BY EMP.EMPLOYEE_ID = PRIOR EMP.MANAGER_ID it would be going up the chain of command instead of down it. There are additional clauses and pseudocolumns which can be added to the hierarchical query which in some cases are necessary to prevent the query from raising an error and in other cases add some additional detail to the output. Using the NOCYCLE clause I’ll start with the NOCYCLE clause; this clause prevents the query from raising an error if the hierarchy in the table is incorrect and ends up looping back on itself which would create an infinite loop Example If the MANAGER_ID field for Kochhar was accidentally set to 108 the following error would occur: “ORA-01436: CONNECT BY loop in user data” This is because 108 is Greenberg who reports to Kochhar, so the query sees the data like this Kochhar GreenBerg Kochhar Greeberg . . . And the loop would just keep going. To allow the query to run instead of showing an error add the NOCYCLE clause right after the CONNECT BY clause Example Select EMP.LAST_NAME, EMP.FIRST_NAME, EMP.EMPLOYEE_ID, EMP.MANAGER_ID From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID CONNECT_BY_ISCYCLE Pseudocolumn Once the NOCYCLE clause has been added to the query to allow it to run a pseudocolumn called CONNECT_BY_ISCYCLE can be used to find the problem. Adding this column to your query will show where the loop is, it does this by displaying a 1 for records that are in the loop and 0 for records that are not. Here is the query with the CONNECT_BY_ISCYCLE clause added. Select EMP.LAST_NAME, EMP.FIRST_NAME, EMP.EMPLOYEE_ID, EMP.MANAGER_ID, CONNECT_BY_ISCYCLE From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID And here are the results from this query LAST_NAME FIRST_NAME EMPLOYEE_ID MANAGER_ID CONNECT_BY_ISCYCLE Kochhar Neena 101 108 0 Whalen Jennifer 200 101 0 Mavris Susan 203 101 0 Baer Hermann 204 101 0 Higgins Shelley 205 101 0 Gietz William 206 205 0 Greenberg Nancy 108 101 1 Faviet Daniel 109 108 0 Chen John 110 108 0 Sciarra Ismael 111 108 0 Urman Jose Manuel 112 108 0 Popp Luis 113 108 0 In the above example the query is displaying 1 in the record associated with Greenberg, so this shows us where the loop is taking place and a correction can be made. SYS_CONNECT_BY_PATH Pseudocolumn The next pseudocolumn is called SYS_CONNECT_BY_PATH; this column is very helpful in illustrating the relationship between records. This pseudocolumn displays a list in each row showing the hierarchical relationship to the record(s) indicated in the START WITH clause. Additionally you can tell the statement what character you want to separate each value with. Here is an example query: Select EMP.LAST_NAME, EMP.FIRST_NAME, EMP.EMPLOYEE_ID, EMP.MANAGER_ID, SYS_CONNECT_BY_PATH(Last_name, '/') Last_name_path, SYS_CONNECT_BY_PATH(EMPLOYEE_ID, '-') ID_PATH From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID And here are the results: LAST_NAME FIRST_NAME EMPLOYEE_ID MANAGER_ID LAST_NAME_PATH ID_PATH Faviet Daniel 109 108 /Kochhar/Greenberg/Faviet , 101, 108, 109 Baer Hermann 204 101 /Kochhar/Baer , 101, 204 Sciarra Ismael 111 108 /Kochhar/Greenberg/Sciarra , 101, 108, 111 Whalen Jennifer 200 101 /Kochhar/Whalen , 101, 200 Chen John 110 108 /Kochhar/Greenberg/Chen , 101, 108, 110 Urman Jose Manuel 112 108 /Kochhar/Greenberg/Urman , 101, 108, 112 Popp Luis 113 108 /Kochhar/Greenberg/Popp , 101, 108, 113 Greenberg Nancy 108 101 /Kochhar/Greenberg , 101, 108 Kochhar Neena 101 100 /Kochhar , 101 Higgins Shelley 205 101 /Kochhar/Higgins , 101, 205 Mavris Susan 203 101 /Kochhar/Mavris , 101, 203 Gietz William 206 205 /Kochhar/Higgins/Gietz , 101, 205, 206 LEVEL Pseudocolumn Another pseudocolumn that can be used in a hierarchical query is called LEVEL. This column displays the number of levels a record is away from the starting record in the hierarchy. So if the START WITH clause has EMPLOYEE_ID = 101 which is Kochhar, Kochhar will be level 1, Greenberg will be level 2 (as would anyone else who reported directly to Kochhar) and Chen would be level 3 along with anyone else who reported to a manager at level 2. Here is an example of a query using the LEVEL pseudocolumn. Select EMP.LAST_NAME, EMP.FIRST_NAME, LEVEL, SYS_CONNECT_BY_PATH(Last_name, '/') Last_name_path, SYS_CONNECT_BY_PATH(EMPLOYEE_ID, ', ') ID_PATH From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID Here are the results: LAST_NAME FIRST_NAME LEVEL LAST_NAME_PATH ID_PATH Kochhar Neena 1 /Kochhar , 101 Whalen Jennifer 2 /Kochhar/Whalen , 101, 200 Mavris Susan 2 /Kochhar/Mavris , 101, 203 Baer Hermann 2 /Kochhar/Baer , 101, 204 Higgins Shelley 2 /Kochhar/Higgins , 101, 205 Gietz William 3 /Kochhar/Higgins/Gietz , 101, 205, 206 Greenberg Nancy 2 /Kochhar/Greenberg , 101, 108 Faviet Daniel 3 /Kochhar/Greenberg/Faviet , 101, 108, 109 Chen John 3 /Kochhar/Greenberg/Chen , 101, 108, 110 Sciarra Ismael 3 /Kochhar/Greenberg/Sciarra , 101, 108, 111 Urman Jose Manuel 3 /Kochhar/Greenberg/Urman , 101, 108, 112 Popp Luis 3 /Kochhar/Greenberg/Popp , 101, 108, 113 Ordering Results using ORDER BY SIBLING If you wanted to order the query result within the Hierarchical structure the ORDER BY SIBLING clause can be used. For example if the you wanted to order by last name however you also wanted to keep related record next to each other this clause would be used. To show the difference here are two queries and the results. One with just the ORDER BY clause and one with the ORDER SIBLINGS BY clause ORDER BY clause query and results: Select EMP.LAST_NAME, EMP.FIRST_NAME, LEVEL, SYS_CONNECT_BY_PATH(Last_name, '/') Last_name_path, SYS_CONNECT_BY_PATH(EMPLOYEE_ID, ', ') ID_PATH From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID ORDER BY Last_name LAST_NAME FIRST_NAME LEVEL LAST_NAME_PATH ID_PATH Baer Hermann 2 /Kochhar/Baer , 101, 204 Chen John 3 /Kochhar/Greenberg/Chen , 101, 108, 110 Faviet Daniel 3 /Kochhar/Greenberg/Faviet , 101, 108, 109 Gietz William 3 /Kochhar/Higgins/Gietz , 101, 205, 206 Greenberg Nancy 2 /Kochhar/Greenberg , 101, 108 Higgins Shelley 2 /Kochhar/Higgins , 101, 205 Kochhar Neena 1 /Kochhar , 101 Mavris Susan 2 /Kochhar/Mavris , 101, 203 Popp Luis 3 /Kochhar/Greenberg/Popp , 101, 108, 113 Sciarra Ismael 3 /Kochhar/Greenberg/Sciarra , 101, 108, 111 Urman Jose Manuel 3 /Kochhar/Greenberg/Urman , 101, 108, 112 Whalen Jennifer 2 /Kochhar/Whalen , 101, 200 ORDER SIBLINGS BY query and results Select EMP.LAST_NAME, EMP.FIRST_NAME, LEVEL, SYS_CONNECT_BY_PATH(Last_name, '/') Last_name_path, SYS_CONNECT_BY_PATH(EMPLOYEE_ID, ', ') ID_PATH From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID ORDER SIBLINGS BY Last_name LAST_NAME FIRST_NAME LEVEL LAST_NAME_PATH ID_PATH Kochhar Neena 1 /Kochhar , 101 Baer Hermann 2 /Kochhar/Baer , 101, 204 Greenberg Nancy 2 /Kochhar/Greenberg , 101, 108 Chen John 3 /Kochhar/Greenberg/Chen , 101, 108, 110 Faviet Daniel 3 /Kochhar/Greenberg/Faviet , 101, 108, 109 Popp Luis 3 /Kochhar/Greenberg/Popp , 101, 108, 113 Sciarra Ismael 3 /Kochhar/Greenberg/Sciarra , 101, 108, 111 Urman Jose Manuel 3 /Kochhar/Greenberg/Urman , 101, 108, 112 Higgins Shelley 2 /Kochhar/Higgins , 101, 205 Gietz William 3 /Kochhar/Higgins/Gietz , 101, 205, 206 Mavris Susan 2 /Kochhar/Mavris , 101, 203 Whalen Jennifer 2 /Kochhar/Whalen , 101, 200 As can be seen in the above results the Ordering will be more meaningful using the ORDER SIBLINGS BY clause instead of just the ORDER BY clause. USING THE LPAD FUNCTION A useful function in displaying the Hierarchical relationship in the query results is the lpad character function which can be used in conjunction with the LEVEL pseudocolumn to help display hierarchical relationship visually. Here is an example of a query using the lpad function Select Lpad(' ',2*(level-1)) ||Last_name ||', '||First_name "LIST", LEVEL, SYS_CONNECT_BY_PATH(Last_name, '/') Last_name_path, SYS_CONNECT_BY_PATH(EMPLOYEE_ID, ', ') ID_PATH From HR.EMPLOYEES EMP Start with EMP.EMPLOYEE_ID = 101 CONNECT BY NOCYCLE PRIOR EMP.EMPLOYEE_ID = EMP.MANAGER_ID ORDER SIBLINGS BY Last_name. Here is how the results are displayed: LIST LEVEL LAST_NAME_PATH ID_PATH Kochhar, Neena 1 /Kochhar , 101 Baer, Hermann 2 /Kochhar/Baer , 101, 204 Greenberg, Nancy 2 /Kochhar/Greenberg , 101, 108 Chen, John 3 /Kochhar/Greenberg/Chen , 101, 108, 110 Faviet, Daniel 3 /Kochhar/Greenberg/Faviet , 101, 108, 109 Popp, Luis 3 /Kochhar/Greenberg/Popp , 101, 108, 113 Sciarra, Ismael 3 /Kochhar/Greenberg/Sciarra , 101, 108, 111 Urman, Jose Manuel 3 /Kochhar/Greenberg/Urman , 101, 108, 112 Higgins, Shelley 2 /Kochhar/Higgins , 101, 205 Gietz, William 3 /Kochhar/Higgins/Gietz , 101, 205, 206 Mavris, Susan 2 /Kochhar/Mavris , 101, 203 Whalen, Jennifer 2 /Kochhar/Whalen , 101, 200
Leave a comment
Categories
News
Careers
Passionate, talented, ambitious, creative … sound familiar?
Three Pillar Global is hiring the best and brightest, and we might just be looking for you.
Check out our open positions.

