How to define the number of levels in a hierarchy using sql? - sql

My situation is the following. I have a table containing a product hierarchy. The following table is an extract of the dataset:
child parent
1 2
2 3
4 5
6 7
I want to add a column containing the depth (of the child) of the hierarchy. Something as follows:
child parent depth
1 2 2
2 3 1
4 5 1
6 7 1
How would I do that in oracle? Thank you!

Something like:
SELECT child, parent, level
FROM your_table
START WITH parent NOT IN ( SELECT child FROM your_table )
CONNECT BY parent = PRIOR child;
Outputs:
CHILD PARENT LEVEL
----- ------ -----
1 2 2
2 3 1
4 5 1
6 7 1

Related

BOM Explosion with Blanks

I have a simple BOM table that contains a parent and a child column.
Every row contains values. There are no blanks.
To bring that table into a BI-Tool I need to add blank values for the parents, like here shown:
As you can see, if an EntityKay has no parent key, then there should be a blank value.
How would you do that?
Example:
Current State:
Child Parent
4 1
5 1
6 2
7 3
8 3
9 3
Needed Result:
Child Parent
1
2
3
4 1
5 1
6 2
7 3
8 3
9 3
This query provides the missing keys, i.e. the parents, that are no childs are selected with the parent null
select distinct Parent Child, null Parent from bom
where Parent not in (select Child from bom)
order by 1;
CHILD PARENT
---------- ------
1
2
3
You must add those records (with an INSERT) to your table and probably update additionaly the description.
insert into bom (Child, Parent)
select distinct Parent Child, null Parent from bom
where Parent not in (select Child from bom)
Result
select * from bom;
CHILD PARENT
---------- ----------
4 1
5 1
6 2
7 3
8 3
9 3
2
3
1
To get the extended data without modifying the table use an UNION ALL of the ofiginal table with the query providing the missing part.
You may add also the names for the new rows using the DECODE as show below in the example
select
Child, Parent, name
from BOM union all
select distinct
Parent Child, null Parent,
decode(Parent,1,'North America',2,'Europa',3,'Asia')
from bom
where Parent not in (select Child from bom)

Oracle SQL index rows based on group by/ parent row

I have 1 table, which refers by value to rows in the same table
Example table:
ID PARENT_ID NAME
1 0 john
2 1 jane
3 2 smigy
4 2 gujo
5 1 duby
6 1 ruby
7 5 foo
8 2 bar
9 3 baz
10 3 qux
The root-parent has parent 0 (just so it wouldn't be null), in this case there's
1 root-parent - parent(0)=1.
root-parent has 1lvl children - parent(1)=2;5;6.
1lvl children has 2lvl children - parent(2)=3;4;8. parent(5)=7. parent(6) has nothing.
2lvl children has 3lvl children - parent(3)=9;10. parent(4) has nothing. parent(8) has nothing.
There is no lvl4 children or anything with depth beyond 4.
And I need to create a script (presumably SQL query - need to avoid function/procedure/etc.) that would index rows based on their position under their parent.
Just like if I'd select all root-parent's and get (rownum-1)
The goal table should look like this:
ID PARENT_ID NAME ROW_INDEX
1 0 john 0
2 1 jane 0
3 2 smigy 0
4 2 gujo 1
5 1 duby 1
6 1 ruby 2
7 5 foo 0
8 2 bar 2
9 3 baz 0
10 3 qux 1
I'm planing to add this column and thus the query will be executed only once. I've played by selecting seperate depth rows, but then I don't really know how to count inside/between group by (even if that is possible).
P.S. A better/good column name suggestion would also be very appreciated.
User row_number()
select mt.*, row_number() over(partition by parent_id order by id) - 1 as rn
from MyTable mt

Oracle: Usage the CONNECT BY statement without START WITH

I'm having some difficulties in the usage of the CONNECT BY statement with Oracle. I have no problem in writing the query for 1 given record (using START WITH) but I'm looking for a possibility to create an extract.
Data
1
2
3
4
5
6
Table
ID PARENT_ID
1 2
1 5
1 6
2 3
2 4
What I need is a table which would be filled with the following information/records.
Desired Output
ID PARENT_ID
1 2
1 3
1 4
1 5
1 6
2 3
2 4
If I use the following query
SELECT PARENT_ID
FROM TABLE
START WITH ID = 1
CONNECT BY NOCYCLE PRIOR ID = PARENT_ID
I will have the following result
2
3
4
5
6
This is effectively the parent_id's which I am looking for for ID 1.
If I remove the start with I have the following list
2
3
4
5
6
3
4
I've tried to add the ID in the statement but that did not work either. Any idea if it's possible to have such an output?
You forgot the prior keyword. You want to connect the parent_id to the id of the previous record. (Could be the other way around, but you'll notice that fast enough. ;-) )
SELECT ID
FROM TABLE
START WITH ID = 1
CONNECT BY prior ID = PARENT_ID

Oracle , select relation

I have 2 tables in oracle DB: Items and Relationship.
items:
ID
---
1
2
3
4
5
6
7
relationship:
ID parent child
--------------------
1 1 2
2 1 3
3 1 4
4 2 5
5 2 6
6 3 7
In the relationship table, I'm storing the hierarchial structure of the "items" (do not ask why it's stored in different tables).
The question:
When I execute this query:
SELECT PARENT_ID, CHILD_ID, CONNECT_BY_ISLEAF, MAX(LEVEL) OVER () + 1 - LEVEL as rev_level
FROM relationship
CONNECT BY PRIOR PARENT_ID = CHILD_ID
START WITH CHILD_ID = 7;
I do not see the root parent because he doesn't exist in this table as a child.
The question is how can I add the root parent(ID = 1) to the query result or join it whith the "items" table and keep the result columns (level and isleaf).
CONNECT_BY_ISLEAF works the other way around when using bottom up search (see this link http://technology.amis.nl/2009/11/14/oracle-11gr2-alternative-for-connect_by_isleaf-function-for-recursive-subquery-factoring-dedicated-to-anton/)
I assume that you want to show more data about the item (like name) If that is the case just left join the items table.
SELECT PARENT_ID AS PARENT_ID,CHILD_ID, i.name AS CHILD_NAME,
CONNECT_BY_ISLEAF,
MAX(LEVEL) OVER () + 1 - LEVEL AS rev_level
FROM items i
LEFT JOIN relationship r ON (i.id = r.CHILD_ID)
CONNECT BY PRIOR PARENT_ID = CHILD_ID
START WITH CHILD_ID = 7
ORDER BY REV_LEVEL;
check this SQLfiddle: http://sqlfiddle.com/#!4/5c9fa/17
In addition check this post about bottom up searches (http://bitbach.wordpress.com/2010/10/18/implementing-bottom-up-path-traversal-for-hierarchical-tables/)
Notice that you have both directions - parent and child.
pick one and dont mix the two.
1 with x as (
2 select 1 as id, 1 as parent, 2 as child from dual union all
3 select 2, 1 , 3 from dual union all
4 select 3 ,1, 4 from dual union all
5 select 4 ,2, 5 from dual union all
6 select 5 ,2, 6 from dual union all
7 select 6 ,3, 7 from dual)
8 select *
9 from x
10 sTART WITH child = 7
11* CONNECT BY PRIOR id= CHILD
SQL> /
ID PARENT CHILD
---------- ---------- ----------
6 3 7
5 2 6
4 2 5
3 1 4
2 1 3
1 1 2
connection is made by prior id = child and not prior parent = child

SQL: Hierarchical query with multiple roots / parents

I have a table describing elements organized in a tree-like structure:
ID, PARENT_ID, NAME
0 null TOP
1 0 A
2 0 B
3 0 C
4 1 AA
5 2 BA
6 3 CA
7 6 CAA
...
There can be many levels in this hierarchy.
Suppose there is a list of elements (say IDs 2 and 3) for which I would like to get all child records from the table.
Something like this:
select *
from MY_TABLE
start with PARENT_ID in (2,3)
connect by PARENT_ID = prior ID
will return:
ID, PARENT_ID, NAME
5 2 BA
6 3 CA
7 6 CAA
However, I want the each output record to be mapped to the original parent from my list (2,3) so that the output would look like this:
ORIGINAL_PARENT_ID, ID, PARENT_ID, NAME
2 5 2 BA
3 6 3 CA
3 7 6 CAA
How can it be done?
connect_by_root may be what you're after?
SQL> select t.*, connect_by_root parent_id as ORIGINAL_PARENT_ID
2 from MY_TABLE t
3 start with PARENT_ID in (2,3)
4 connect by PARENT_ID = prior ID
5 /
ID PARENT_ID NAM ORIGINAL_PARENT_ID
---------- ---------- --- ------------------
5 2 BA 2
6 3 CA 3
7 6 CAA 3
Assuming your names are really as you have them, then the problem can be done without connect by. You can use simple string manipulation.
with ToFind (
select 'C' as parent from dual union all
select 'B' as parent from dual
)
select t.*
from t join
ToFind tf
on t.name like tf.parent, 100)||'%' and t.name <> tf.parent