how i do this in oracle pl/sql - sql

I have the follow table: example:
nodes:
id_node id_parent
--------------------
1 3
3 2
2 -1
I want to insert in other table the level of descent. For example:
ancestor:
id_node id_parent level
-------------------------
1 3 1
3 2 1
1 2 2

Should be something like this:
SELECT id_node, id_parent, level
FROM nodes
START WITH id_parent = -1
CONNECT BY PRIOR id_node = id_parent

Related

Hierarchical Data In Oracle SQL

I have tbl_parent like this in Oracle SQL, sample data is given below:
Id(primary key) parentid childid
1 1 2
2 1 3
3 2 1
4 3 1 -- This row is wrong
In above table, some rows are incorrectly inserted, for example, if parent_id 1 has child_id 3 then parent_id 3 should not have child_id 1 as 3 is already child of 1 so can not be parent, I have 5000+ rows and want to find these incorrect rows, any help please?
greatest and least functions might be used as
select least(parentid,childid) as least_par_chi_id,
greatest(parentid,childid) as greatest_par_chi_id
from tab
group by greatest(parentid,childid), least(parentid,childid)
having count(*)>1;
Basically you are looking for cycles in your table.
The Oracle functionality to indentify cycles in hierarchical query is
CONNECT BY NOCYCLE and CONNECT_BY_ISCYCLE
This query show all nodes that lead to cycle - column is_Cycle = 1
select tbl.* ,
CONNECT_BY_ISCYCLE is_Cycle,
SYS_CONNECT_BY_PATH(childid, '/') path
from tbl
CONNECT BY NOCYCLE PRIOR childid = parentid
For your data the result is
PARENTID CHILDID IS_CYCLE PATH
---------- ---------- ---------- ----------
1 2 0 /2
2 1 1 /2/1
1 3 1 /2/1/3
1 3 0 /3
3 1 1 /3/1
1 2 1 /3/1/2
2 1 0 /1
1 2 1 /1/2
1 3 1 /1/3
3 1 0 /1
1 2 1 /1/2
1 3 1 /1/3
Note taht each cycle is recognised on several places, so you get some redundant data.
The advantage of this apprach is, that it works for longer cycles too (where the simple GROUP BY approach fails).
Example for cycle of the length 3:
create table tbl as
select 1 parentid, 2 childid from dual union all
select 2 parentid, 3 childid from dual union all
select 3 parentid, 1 childid from dual;
PARENTID CHILDID IS_CYCLE PATH
---------- ---------- ---------- ----------
1 2 0 /2
2 3 0 /2/3
3 1 1 /2/3/1
2 3 0 /3
3 1 0 /3/1
1 2 1 /3/1/2
3 1 0 /1
1 2 0 /1/2
2 3 1 /1/2/3

How to define the number of levels in a hierarchy using 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

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

Need to combining two column's data into one column in SQL?

I am providing one example here to make you all understand what i actually need:
I have a table like this:
Name Null? Type
----------------------------------------- -------- --------------
Child_ID NUMBER(10)
Father_ID NUMBER(10)
Values are:
Child_ID Father_ID
---------- ----------
2 1
4 1
3 2
5 3
Now I want the hierarchical information for Father id 1. For that I have wrote one query and that is providing me the exact output:
**select * from child
start with father_id=1
connect by prior child_id = father_id;**
O/P:
Child_ID Father_ID
---------- ----------
2 1
3 2
5 3
4 1
Now I want the o/p should be like this:
ID
-----
1
2
3
4
5
I can get it easily by using union key but I don't want to use that.
Is there any other way to get this?
Thanks in advance.
It's a bit messy but you could do :
select distinct case when l=1 then child_id else father_id end id
from
(select child_id, father_id
from child
start with father_id=1 connect by prior child_id=father_id) child
cross join (select level l from dual connect by level < 3)
order by id
;

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