
Hieararchies or trees in Oracle using
CONNECT BY
is actually pretty fun to work with. Lately I have been a real
“tre-hugger”, since I have worked a lot with different tree structures,
and downlines. I thought I wanted to share some of my latest
experiences.
Introduction
In this post I want to cover some of the things that have a bit interesting for me lately. Things like:
- How to visually draw a hierarchy?
- What is the difference between table count and downline count?
- How to sort a CONNECT BY query best possible?
- How to get bottom left side or bottom right side node for a given node?
- How to get the upline ids for a given node?
Before
we go on I would just want to mention that for this tutorial I threw
together some data in two different tables: CUST and DOWNLINE_TEST.
2 | Name Type Nullable Default Comments |
38 | SQL> desc downline_test; |
39 | Name Type Nullable Default Comments |
53 | NODE_ID PARENT_ID CUST_ID LEFT_ID RIGHT_ID SIDE |
55 | 1001 1000 1 1002 1003 R |
56 | 1002 1001 2 1004 1005 L |
57 | 1003 1001 3 1006 1007 R |
58 | 1004 1002 4 1008 1009 L |
59 | 1005 1002 5 1010 1011 R |
60 | 1006 1003 6 1012 1013 L |
61 | 1007 1003 7 1014 1015 R |
63 | 1009 1004 9 1018 1019 R |
69 | 1015 1007 15 1030 1031 L |
73 | 1031 1015 31 1062 1062 L |
This image shows the downline visually:
Alrighty then, let’s start answering some of the initial questions.
How to visually draw a hierarchy?
In
a project I have worked on a bit the last little while, my task has
been to generate a binary tree based on data in a different type of
tree. There have been many factors involved, like which people should be
placed before others in the tree, types of customers, etc., etc.
While
you are working your way through the code and see them records actually
be generated, it sometimes is nice to visually make a “right answer” to
compare with the data you have generated. I was looking around on the
Internet a while ago for different tools to use, and found two ways to
make a visual tree electronically – in addition to the most obvious
method to use.
Drawing hierarchy by hand
This is actually
the method I have used most, and it works pretty well, but sometimes it
can be pretty tight between the nodes if you do not plan well, and if
you mess up…well…toss the paper and start over.
Drawing a hierarchy using Kap Lab’s Kalileo
In
the beginning I used this online tool a LOT, since I had to visually
show trees that had up to 250-300 nodes in it. The tool is actually a
demo for an object you can use with Adobe Flashbuilder (Flex). You can
find the demo site for
Kalileo here:
http://lab.kapit.fr/media/demo/kalileo/prod/index.php
One
problem I found using this tool though was that it seems to use some
sort of logic when it redraws the hierarchy you just used an hour to
create…and what was on the left side is now on the right side, etc. That
did not work well for me, and one time I used the tool for almost three
hours and was hoping and praying that the power did not go out…since
when you save it and reopen it everything looks different.
Drawing a hierarchy using Excel’s “SmartArt”
This tool is what I have been using most lately. You can use “
SmartArt”
in Word as well, but Excel is best because of the “space”. You can find
this in the newer versions of Excel under “Insert” and then “SmartArt”.
Here you can choose between several types of hierarchies, etc. I really
like this method, and it is very simple to use.The picture of the
downline above is made using Excel and “SmartArt”.
The only thing I could not figure out was how to get rid of the 3D look completely, but oh well…it works pretty well.
What is the difference between table count and downline count?
Ok, I guess this might be pretty obvious, but just a reminder. Let us say that you have generated 21 records in your
node /
hierarchy/
downline
table, like in the drawing above. If you would want to make sure that
you got everything, you would want to count the number of records in the
downline_test table:
…and then compare it to the count of records your
CONNECT BY sql would give you:
2 | 2 FROM downline_test dl |
3 | 3 START WITH dl.node_id = 1001 |
4 | 4 CONNECT BY PRIOR dl.node_id = dl.parent_id |
This
is of course just an example, and if you have many downlines in your
tree, and you want to test counts for only a part of the tree, you would
of course have to filter your records accordingly.
But, these numbers need to match. If they do not, something is wrong in your tree.
How to sort a CONNECT BY query best possible?
You can sort your
CONNECT BY
query in whatever way you want, but if you (like me) work for a company
where the left side should be presented before the right side, etc….it
is sometimes nice to sort your query like this:
11 | 11 WHERE c.cust_id = dl.cust_id |
12 | 12 START WITH dl.node_id = 1001 |
13 | 13 CONNECT BY PRIOR dl.node_id = dl.parent_id |
14 | 14 ORDER SIBLINGS BY dl.side |
17 | LVL NODE_ID CUST_ID CUST_NAME PARENT_ID LEFT_ID RIGHT_ID SIDE |
19 | 1 1001 1 BILL 1000 1002 1003 R |
20 | 2 1002 2 BOB 1001 1004 1005 L |
21 | 3 1004 4 JALENE 1002 1008 1009 L |
22 | 4 1008 8 JASMINE 1004 L |
23 | 4 1009 9 JOHN 1004 1018 1019 R |
26 | 3 1005 5 LISA 1002 1010 1011 R |
28 | 4 1011 11 BRANDON 1005 R |
29 | 2 1003 3 BARRY 1001 1006 1007 R |
30 | 3 1006 6 ROBERT 1003 1012 1013 L |
32 | 4 1013 13 STEVE 1006 R |
33 | 3 1007 7 CHRIS 1003 1014 1015 R |
34 | 4 1014 14 MARSHALL 1007 L |
35 | 4 1015 15 JUSTIN 1007 1030 1031 R |
37 | 5 1031 31 CARLA 1015 1062 1062 R |
39 | 6 1063 63 STUART 1031 R |
In a hierarchy like shown under the “
Introduction”
part in this tutorial, each person has two legs. Left leg, and right
leg. So, for Mr. Top Guy – “BILL” , his left leg consists of “BOB”,
“JALENE”, and “JASMINE”. BILL’s right leg consist of “BARRY”, “CHRIS”,
“JUSTIN”, “CARLA”, and “STUART”.
So, in the query I just showed
you above, the data will be presented left side all the way down, and
then the right side. This is done using “
ORDER SIBLINGS BY
dl.side”, and “L” comes before “R” in the alphabet. Pretty handy. I had
never used this until this year, even if I have worked with hierarchies
many times during the years as an Oracle developer.
How to get to bottom left side or bottom right side for a given node?
Let
us say that our Mr Cool Guy – “BILL” is about to sign up a new person,
and he wants to sign this person up on his left side. Below is a query
you can use for this:
2 | 2 FROM downline_test dl |
3 | 3 START WITH dl.node_id = 1001 |
4 | 4 CONNECT BY PRIOR dl.node_id = dl.parent_id AND dl.side = 'L' |
Since
we actually want to place our new person on the left side of “JASMINE”,
we are not totally done yet, fact being that more than one record is
returned. This is a sample on how you can get the bottom left node under
“BILL”:
5 | 5 , MAX ( LEVEL ) OVER (PARTITION BY 1) max_lvl |
6 | 6 FROM downline_test dl |
7 | 7 START WITH dl.node_id = 1001 |
8 | 8 CONNECT BY PRIOR dl.node_id = dl.parent_id AND dl.side = 'L' |
10 | 10 WHERE x.lvl = x.max_lvl |
There you have it. And, of course, if you want the bottom right side, just switch out the ‘L’ with an ‘R’ in the query above.
Also, to find the MAX level I am using an analytic function. You can read more about analytic functions here:
How To Use Oracle Analytic Functions in Oracle SQL
And then, last..but least:
How to get the upline ids for a given node?
Ok, so you have been generating records like crazy in your
downline
table, and suddenly there is an error. You see that it appears under
node “X”, but the problem is actually caused due to some placements
under one of the upline customers of the customer under node “X”. So, a
very nice thing to know then is: How do we get the
upline list for “X”?
It is actually pretty simple:
11 | 11 WHERE c.cust_id = dl.cust_id |
12 | 12 START WITH dl.node_id = 1015 |
13 | 13 CONNECT BY dl.node_id = PRIOR dl.parent_id |
16 | LVL NODE_ID CUST_ID CUST_NAME PARENT_ID LEFT_ID RIGHT_ID SIDE |
18 | 1 1015 15 JUSTIN 1007 1030 1031 R |
19 | 2 1007 7 CHRIS 1003 1014 1015 R |
20 | 3 1003 3 BARRY 1001 1006 1007 R |
21 | 4 1001 1 BILL 1000 1002 1003 R |
The difference between showing downline, and upline is the keyword “
PRIOR“. So, if we move “
PRIOR” to the left side, we will get the downline:
11 | 11 WHERE c.cust_id = dl.cust_id |
12 | 12 START WITH dl.node_id = 1015 |
13 | 13 CONNECT BY PRIOR dl.node_id = dl.parent_id |
16 | LVL NODE_ID CUST_ID CUST_NAME PARENT_ID LEFT_ID RIGHT_ID SIDE |
18 | 1 1015 15 JUSTIN 1007 1030 1031 R |
20 | 2 1031 31 CARLA 1015 1062 1062 R |
22 | 3 1063 63 STUART 1031 R |
Summary
In
summary: Make sure you have no holes or missing nodes in your tree by
comparing the raw table count agains the count of records of your
CONNECT BY query. Also, by moving the PRIOR keyword between your left
and right side of the “=” in your CONNECT BY line, you will be able to
switch between showing a node’s downline, or upline.
No comments :
Post a Comment