1

I am new to this topic (i.e tree structure) in SQL. I have gone through different sources but still not clear.

Here in my case, I have a table which I have attached herewith.

enter image description here

Now here first, I have to retrieve a Full Tree for “OFFICE”.

Also i have to find all the leaf nodes (those with no Children) in the attached hierarchical data.

Please provide the answers with detail explanation. Thanks in advance.

Suniel
  • 1,449
  • 8
  • 27
  • 39

4 Answers4

3

You didn't specify your DBMS but with standard SQL (supported by all modern DBMS) you can easily do a recursive query to get the full tree:

with recursive full_tree as (
  select id, name, parent, 1 as level
  from departments
  where parent is null
  union all 
  select c.id, c.name, c.parent, p.level + 1
  from departments c
    join full_tree p on c.parent = p.id
)
select *
from full_tree;

If you need a sub-tree, just change the starting condition in the common table expression. To get e.g. all "categories":

with recursive all_categories as (
  select id, name, parent, 1 as level
  from departments
  where id = 2 --- << start with a different node 
  union all 
  select c.id, c.name, c.parent, p.level + 1
  from departments c
    join all_categories p on c.parent = p.id
)
select *
from all_categories;

Getting all leafs is straightforward: it's all nodes where their ID does not appear as a parent:

select *
from departments
where id not in (select parent
                 from departments
                 where parent is not null);

SQLFiddle example: http://sqlfiddle.com/#!15/414c9/1


Edit after DBMS has been specified.

Oracle does support recursive CTEs (although you need 11.2.x for that) you just need to leave out the keyword recursive. But you can also use the CONNECT BY operator:

select id, name, parent, level
from departments
start with parent is null
connect by prior id = parent;

select id, name, parent, level
from departments
start with id = 2
connect by prior id = parent;

SQLFiddle for Oracle: http://sqlfiddle.com/#!4/6774ee/3

See the manual for details: https://docs.oracle.com/database/121/SQLRF/queries003.htm#i2053935

2

Now here first, I have to retrieve a Full Tree for “OFFICE”.

How do you want to represent your tree? What you have posted can already count as a tree.

Also I have to find all the leaf nodes (those with no Children) in the attached hierarchical data.

TSQL:

select * 
from table outer 
where id not in (select id 
                 from table inner 
                 where inner.parent = outer.id)

The inner select will give you all ids pointing to a certain parent. The outer select will give you all nodes for which the inner select yields no results.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
wotanii
  • 2,470
  • 20
  • 38
2

There is a pattern design called "hardcoded trees" that can be usefull for this purpose. enter image description here

Then you can use this query for finding a parent for each child

SELECT level1ID FROM Level2entity as L2 WHERE level2ID = :aLevel2ID

Or this one for finding the children for each parent:

SELECT level1ID FROM Level2entity as L2 WHERE level2ID = :aLevel2ID
Luis Teijon
  • 4,769
  • 7
  • 36
  • 57
2

If you can afford a little bit of pre-processing time, then a classic solution to this problem would be to use nested sets.

CREATE TABLE node (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    parent_id INTEGER DEFAULT NULL,
    seq_min INTEGER NOT NULL,
    seq_max INTEGER NOT NULL
); 

Assuming, that you use some kind of in-memory data structure like

class Node:
   def __init__(self, id, name, children=()):
      self.id = id
      self.name = name
      self.children = list(children)

to represent the tree nodes in memory, we can then define

def assign_sequence_numbers(root_node):
    def recurse(counter, node):
        node.seq_min = counter
        node.seq_max = reduce(recurse, node.children, 1 + counter)
        return node.seq_max
    return recurse(1, root_node)

which does a pre-order traversal over the tree, which assignes two additional numbers to each node:

The seq_min value becomes an alternate unique integer key for each node. Also, all direct children (and indirect descendants) of a node will have a seq_min value, which is larger than the value assigned to the node itself.

The seq_max value is simply an upper boundary for the seq_min values of all descendant nodes.

Example: Consider the following tree

Root
 |
 +--- Category1
 |      |
 |      +--- Category1.1
 |      |
 |      `--- Category1.2
 |
 `--- Category2

Using the function defined above, we get

Root [min=1, max=6]
 |
 +--- Category1 [min=2, max=5]
 |      |
 |      +--- Category1.1 [min=3, max=4]
 |      |
 |      `--- Category1.2 [min=4, max=5]
 |
 `--- Category2 [min=5, max=6]

Observe, that for every node, the min value is always <= the min value of all descendants and the max value is always > the max value of all descendants. So, to find the descendants of Root and that node itself (i.e., get the entire tree), we'd do:

SELECT * FROM node 
WHERE seq_min >= 1 /* i.e., seq_min(Root) */
AND seq_min < 6    /* i.e., seq_max(Root) */

To get the descendants of Category1 (and that node itself)

SELECT * FROM node 
WHERE seq_min >= 2 /* i.e., seq_min(Category1) */
AND seq_min < 5    /* i.e., seq_max(Category2) */

The drawback of this method is, that in principle, you will have to re-assign the seq_xxx numbers for all nodes, if you make changes to the tree, i.e., insert new nodes or delete old ones. This is often not a problem, at least if changes to the tree structure are infrequent and the tree is sufficiently small.

As ITGenius already linked to that answer: if your database supports it, using common table expressions is another possibility, which avoids the pre-processing step and is also more resilient in the face of changes to the tree structure.

(I did not test any of the code given in this answer. You might need to apply a few fixes here and there; in particular, if your project's language of choice is not python...)

Community
  • 1
  • 1
Dirk
  • 30,623
  • 8
  • 82
  • 102