Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Abhijit.

Asked: July 04, 2016 - 11:35 am UTC

Last updated: July 05, 2016 - 4:46 pm UTC

Version: 12c

Viewed 1000+ times

You Asked

Hello ,
I have one question which are asked into interview ,To make a tree when user insert a node into table its path get automatically reflected into table
Table:
Tree
----------------------
node(int) parentNode(int) path(ltree)
--------------------------------------
1 null
2 1
3 1
4 2
5 2
6 3
7 3
8 3

U have to solve this problem using only trigger

REQUIRED OUTPUT:

Tree
----------------------
node(int) parentNode(int) path(ltree)
--------------------------------------
1 null 1
2 1 1.2
3 1 1.3
4 2 1.2.4
5 2 1.2.5
6 3 1.3.6
7 3 1.3.7
8 3 1.3.8

1) If someone update a node or parent node its path get changed
for ex user want to change parent node of 8
So its parent become 7 and its path become 1.3.7.8

2)If someone delete a node 3 so its child node become parent node of 3

Please ! Give me solution

and Chris said...

If it's an interview question and I answer it, do I get the job? ;)

To get the path, use a hierarchical query.

Here's an example using traditional connect by. With this an sys_connect_by_path, you can get the path to the root for each node:

create table t (
  node int,
  parentn int
);

insert into t values (1, null);
insert into t values (2, 1);
insert into t values (3, 1);
insert into t values (4, 2);
insert into t values (5, 2);
insert into t values (6, 3);
insert into t values (7, 6);

select node, parentn, sys_connect_by_path(node, ';') path 
from t
start with parentn is null
connect by prior node = parentn;

NODE  PARENTN  PATH      
1              ;1        
2     1        ;1;2      
4     2        ;1;2;4    
5     2        ;1;2;5    
3     1        ;1;3      
6     3        ;1;3;6    
7     6        ;1;3;6;7


You can also do this using the with clause:

https://oracle-base.com/articles/11g/recursive-subquery-factoring-11gr2

To handle changes, the naive solution is to recompute all the paths every time:

alter table t add (path varchar2(100));

update t
set    parentn = 4
where  node = 5;

update t
set    path = (
with paths as (
  select node, parentn, sys_connect_by_path(node, ';') path 
  from t
  start with parentn is null
  connect by prior node = parentn
) 
 select path from paths p
 where  p.node = t.node
);

select * from t;

NODE  PARENTN  PATH      
1              ;1        
2     1        ;1;2      
3     1        ;1;3      
4     2        ;1;2;4    
5     4        ;1;2;4;5  
6     3        ;1;3;6    
7     6        ;1;3;6;7


This inefficient. There are better approaches available. They come with their own trade-offs though. Bill Karwin discusses various method at:

http://www.slideshare.net/billkarwin/models-for-hierarchical-data

Improving this and implementing using a trigger left as an exercise for the interviewee ;)

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

CONNECT_BY_ROOT

Rajeshwaran Jeyabal, July 05, 2016 - 12:50 pm UTC

Other alternatives would be to use CONNECT_BY_ROOT option.

set feedback off
drop table t purge;
create table t(node int, parentn int);
insert into t values(1,null); 
insert into t values(2,1); 
insert into t values(3,1);
insert into t values(4,2); 
insert into t values(5,2); 
insert into t values(6,3); 
insert into t values(7,3); 
insert into t values(8,3);
commit; 
set feedback on 


demo@ORA11G> select t.* , connect_by_root node,level
  2  from t
  3  start with parentn is null
  4  connect by prior node = parentn
  5  order by level,1 ;

      NODE    PARENTN CONNECT_BY_ROOTNODE      LEVEL
---------- ---------- ------------------- ----------
         1                              1          1
         2          1                   1          2
         3          1                   1          2
         4          2                   1          3
         5          2                   1          3
         6          3                   1          3
         7          3                   1          3
         8          3                   1          3

8 rows selected.

demo@ORA11G> column x format a10
demo@ORA11G> select node,parentn,
  2        case when to_char(level) = '1' then to_char(node)
  3             when to_char(level) <='2' then parentn||'.'||node
  4            else connect_by_root node||'.'||parentn||'.'||node end x
  5  from t
  6  start with parentn is null
  7  connect by prior node = parentn
  8  order by level,1 ;

      NODE    PARENTN X
---------- ---------- ----------
         1            1
         2          1 1.2
         3          1 1.3
         4          2 1.2.4
         5          2 1.2.5
         6          3 1.3.6
         7          3 1.3.7
         8          3 1.3.8

8 rows selected.

demo@ORA11G>

Chris Saxon
July 05, 2016 - 4:46 pm UTC

Yeah, but it's kludgy compared to sys_connect_by_path. Why would you use this instead?

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library