0

I have a oracle database where foreign key is enabled and I have tables A,B,C,D,E,F,AB,ABC,EF Attaching the picture for hierarchy 'B' has parent 'D', 'AB' has parent 'A' as well as 'B', 'ABC' has parent 'AB' and 'C', 'EF' has parent 'E' & 'F',

enter image description here

Now {A,B,C,D,AB, ABC} has no link with {E,F,EF}. I have 2 requirements

  1. I need to group them as group 1 & 2
  2. I need to assign a hierarchy level for each group maintaining referential integrity as follows

enter image description here

How can I do it using sql/pl/sql in oracle database?

MT0
  • 143,790
  • 11
  • 59
  • 117
  • Before we get too far - The first part asks for finding the connected components in a graph. The graph nodes, in your case, are the tables, and the graph edges (arrows, links) are the referential constraints. This is a common problem; SQL chokes pretty quickly on it (it can't deal with graphs with more than 30-40 nodes in most cases, in any reasonable amount of time). Oracle offers packages that solve this problem. I offered a solution that performs much faster than Oracle's solution here: https://community.oracle.com/tech/developers/discussion/4344746/les-amis-de-mes-amis-sont-mes-amis#latest –  Mar 05 '21 at 02:05
  • In the most general case, the second part of your question doesn't make sense. Referential constraints may have cycles (for example: the "cities" table has a column "country"; the "countries" table has a column "capital city" referencing the "cities" table. So, in this case, which is the first level and which is the second? And the references may be quite a bit more complicated. For example: table A references both B and C, and also table B references C. Table C is level 1, and table B is level 2, but what level is A? Is it 2 also (because it references C), or 3 (because it also references B)? –  Mar 05 '21 at 02:12

1 Answers1

0

Assuming that you always end up at a single leaf node for each group then you can use a hierarchical query twice: from leaf-to-root to determine the groups; and then from root-to-leaf to determine the depths.

WITH groups ( child, parent, grp ) AS (
  SELECT child,
         parent,
         DENSE_RANK() OVER ( ORDER BY CONNECT_BY_ROOT( child ) )
  FROM   table_name t
  START WITH child NOT IN ( SELECT parent FROM table_name )
  CONNECT BY PRIOR parent = child
),
depths ( child, parent, grp, depth ) AS (
  SELECT child, parent, grp, LEVEL
  FROM   groups
  START WITH parent NOT IN ( select child FROM table_name )
  CONNECT BY PRIOR child = parent
)
SELECT table_name,
       grp,
       MAX( depth + depth_modifier ) AS depth
FROM   depths
UNPIVOT ( table_name FOR depth_modifier IN ( child AS 1, parent AS 0 ) )
GROUP BY grp, table_name
ORDER BY grp, depth, table_name

Which, for your sample data:

CREATE TABLE table_name ( child, parent ) AS
SELECT 'B',   'D'  FROM DUAL UNION ALL
SELECT 'AB',  'A'  FROM DUAL UNION ALL
SELECT 'AB',  'B'  FROM DUAL UNION ALL
SELECT 'ABC', 'AB' FROM DUAL UNION ALL
SELECT 'ABC', 'C'  FROM DUAL UNION ALL
SELECT 'EF',  'E'  FROM DUAL UNION ALL
SELECT 'EF',  'F'  FROM DUAL;

Outputs:

TABLE_NAME | GRP | DEPTH
:--------- | --: | ----:
A          |   1 |     1
C          |   1 |     1
D          |   1 |     1
B          |   1 |     2
AB         |   1 |     3
ABC        |   1 |     4
E          |   2 |     1
F          |   2 |     1
EF         |   2 |     2

If you don't always have a single leaf node for each group then you'll need a MUCH more complicated solution (probably written in PL/SQL).

db<>fiddle here

MT0
  • 143,790
  • 11
  • 59
  • 117
  • I have 200 tables, but getting 700 rows after using the query, I was expecting 200 rows. could you please share the pl/sql staff you were mentioning? – user3016635 Mar 05 '21 at 19:08
  • @user3016635 I don't have a PL/SQL solution to hand. You just need to work out how you can do it in Java/C/Python/etc. (without having to deal with a database) and then just translate it to PL/SQL. Its just a depth-first search up and down the tree structure. – MT0 Mar 05 '21 at 20:09