1

I have the following data structure already in the system.

ItemDetails:

ID Name
--------
1  XXX
2  YYY
3  ZZZ
4  TTT
5  UUU
6  WWW

And the hierarchies are in separate table (with many to many relationships)

ItemHierarchy:

ParentCode ChildCode
--------------------
1            2
1            3
3            4
4            5
5            3
5            6

As you can see that 3 is child node for 1 and 3. I want to traverse records say for example that from the node 3.

I need to write a stored procedure and get all the ancestors of 3 and all the child nodes of 3.

Could you please let me know whether any possibilities to pull the data? If so, which data structure is OK for it.

Please note that my table is containing 1 million records and out of it 40% are having multiple hierarchies.

I did 'CTE' with level and incrementing it based upon the hierarchy but I'm getting max recursive error when we traverse from root to leaf level node. I have tried 'HierarchyID' but unable to get all the details when its having multiple parent for a node.

Update: I can set a recursion limit to max and run the query. Since it has millions of rows, I'm unable to get the output at all.

I want to create a data structure such that its capable to giving information from top to bottom or bottom to top (at any node level).

Could someone kindly please help me with that?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
VIRA
  • 1,454
  • 1
  • 16
  • 25
  • Please show your query. If it's correct, then the problem may simply be that your hierarchy is deeper than the default recursion limit. Setting this is explored elsewhere on SE: http://stackoverflow.com/a/7428903/565869. –  Apr 22 '14 at 15:10
  • 3
    The problem with the dat is thet there is a loop: `3 -> 4 -> 5 -> 3` That is not an hierarchy. – joop Apr 22 '14 at 15:12
  • Yes Joop, Its not a hierarchy (its many to many relationship) and we need to make it as a proper data structure. I'm simply tried most of the stuffs and I'm not quite happy with the performance. Any suggestion would be beneficial. – VIRA Apr 22 '14 at 15:16
  • 1
    @Raj - The term you need is `web`. A hierarchy explicitly does not have loops. Can you please describe any rules your data will (or needs to) obey, and what kinds of queries you'll need. *(For example, nested-sets and adjacency-lists both represent hierarchies, but one is much better when searching for all children, but very poor when looking for parents.)* – MatBailie Apr 22 '14 at 15:25
  • What kind of information do you want to retrieve? The parents, the children, the level of the node? Btw a recursive query does not yield results cause its an infinite loop. – Fabian Bigler Apr 22 '14 at 15:48
  • @MatBailie, Yes you are correct. We shall call it as web. It doesn't obey any set of rules though. Its just related with parent and child code. I want to pull the data at any level from root node to leaf and leaf to root. – VIRA Apr 22 '14 at 15:56
  • @Fabian Bigler, I want to pass a ID to proc and it should give me ancestors and descendants. When its multiple child then I need to list them all. same for multiple parents too. – VIRA Apr 22 '14 at 15:58
  • 1
    @Raj - Please could you give examples of "top to bottom" and "bottom to top" with specific consideration to the fact that you have a loop *(3 -> 4 -> 5 -> 3)*; as there is no top or bottom of a loop, what results do you expect? – MatBailie Apr 23 '14 at 09:23
  • @Raj :I made an update to my answer. may come in handy. – Mohsen Heydari May 03 '14 at 11:42

1 Answers1

2

Using RDBMS for hierarchical data structure is not recommended, its why graph database have been created.

BTW following Closure Table pattern will help you.
The Closure Table solution is a simple and elegant way of storing hierarchies. It involves storing all paths through the tree, not just those with a direct parent-child relationship.

The key point to use the pattern is how you must fill ItemHierarchy table.
Store one row in this table for each pair of nodes in the tree that shares an ancestor/descendant relationship, even if they are separated by multiple levels in the tree. Also add a row for each node to reference itself.
Think we have a simple graph like bellow: enter image description here
The doted arrows shows the rows in ItemHierarchy table: enter image description here
To retrieve descendants of #3:

SELECT c.*
FROM ItemDetails AS ID
JOIN ItemHierarchy AS IH ON ID.ID = IH.ChildCode
WHERE IH.ParentCode = 3;

To retrieve ancestors of #3:

SELECT c.*
FROM ItemDetails AS ID
JOIN ItemHierarchy AS IH ON ID.ID = IH.ParentCode 
WHERE IH.ChildCode = 3;

To insert a new leaf node, for instance a new child of #5, first insert the self-referencing row. Then add a copy of the set of rows in TreePaths that reference comment #5 as a descendant (including the row in which #5 references itself), replacing the descendant with the number of the new item: INSERT INTO ItemHierarchy (parentCode, childCode)

SELECT IH.parentCode, 8
FROM ItemHierarchy AS IH
WHERE IH.childCode = 5
UNION ALL
SELECT 8, 8;

To delete a complete sub-tree, for instance #4 and its descendants, delete all rows in ItemHierarchy that reference #4 as a descendant, as well as all rows that reference any of #4’s descendants as descendants:

DELETE FROM ItemHierarchy 
WHERE chidCode IN (SELECT childCode
FROM ItemHierarchy 
WHERE parrentCode = 4);



UPDATE
Since the sample data you have shown us leads to recursive loops(not hierarchies) like:

1 -> 3 -> 4 -> 5 -> 3 -> 4 -> 5

Following Path Enumeration pattern will help you.
A UNIX path like /usr/local/lib/ is a path enumeration of the file system, where usr is the parent of local, which in turn is the parent of lib.
You can create a Table or View from ItemHierarchy table, calling it EnumPath:
Table EnumPath(NodeCode, Path)

For the sample data we will have:
enter image description here
To find ancestors of node #4:

select distinct E1.NodeCode from EnumPath E1
inner join EnumPath E2
On E2.path like E1.path || '%'
where E2.NodeCode = 4 and E1.NodeCode != 4;

To find descendants of node #4:

select distinct E1.NodeCode from EnumPath E1
inner join EnumPath E2
On E1.path like E2.path || '%'
where E2.NodeCode = 4 and E1.NodeCode != 4;

Sqlfiddle demo

Mohsen Heydari
  • 7,256
  • 4
  • 31
  • 46
  • Perhaps you could add how the OP should create a closure table with the OPs example data? Avoiding the infinite loop : `1 -> 3 -> 4 -> 5 -> 3 -> 4 -> 5` *(In particular, do you have any SQL that would transform the OP's adjacency list, including the loop, in to a closure table?)* – MatBailie Apr 22 '14 at 16:13
  • You are right. since the primary key of ItemHierarchy is (parrentCode, childCode) the data like (4,5) + (4,5) wouldn't be acceptable! I just focused on extract of the hierarchy. – Mohsen Heydari Apr 22 '14 at 16:26
  • @MohsenHeydari, Thanks for the detailed answer man. But I'm dealing with millions of records and this will increment my database size to another few. Thanks a lot for the knowledge sharing. – VIRA Apr 22 '14 at 16:41