Manually Create Relationship Tables for Parent-Child Hierarchies Using Scripts
Use the script examples provided in this topic to manually create relationship tables for parent-child hierarchies. When run, the scripts generate and load the relationship tables required for your parent-child hierarchies.
Note:
Oracle recommends that you use the Generate Relationship Table functionality to create the scripts. See Generate Scripts to Create a Parent-Child Relationship Table.The key elements that you must define for a parent-child hierarchy are the identifier columns for the member and the parent of the member. The following needs to be replaced with Member Key column, Parent Key column and the desired relationship table name before executing the script.
${SOURCE_TABLE_NAME}
— Source table with Parent Child data. For example, an Employee or Employee dimension table.${SOURCE_MEMBER_KEY_COL_NAME}
— Key column in the source table. For example,Employee_Key
in Employee table.${SOURCE_PARENT_KEY_COL_NAME}
— Parent Key column in the source table. For example,Manager_Key
in Employee table.${RELATIONSHIP_TABLE_NAME}
— Parent-child relationship table for parent-child hierarchies based on relational tables.${MEMBER_COLUMN_DATA_TYPE}
— Data type of member key column${PARENT_COLUMN_DATA_TYPE}
— Data type of parent key column. The same data type as member key column.
Oracle Database
Example 20-1 Create Relationship Table Script
CREATE TABLE ${RELATIONSHIP_TABLE_NAME} (MEMBER_KEY ${MEMBER_COLUMN_DATA_TYPE} , ANCESTOR_KEY ${PARENT_COLUMN_DATA_TYPE}, DISTANCE NUMBER(10,0), IS_LEAF NUMBER(10,0) );
Example 20-2 Load Relationship Table Script
For details declare
v_max_depth integer;
v_stmt varchar2(32000);
i integer;
begin
select max(level) into v_max_depth
from ${SOURCE_TABLE_NAME}
connect by prior ${SOURCE_MEMBER_KEY_COL_NAME}= ${SOURCE_PARENT_KEY_COL_NAME}
start with ${SOURCE_PARENT_KEY_COL_NAME} is null;
v_stmt := 'insert into ${PARENT_CHILD_TABLE_NAME} (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)' || chr(10)
|| 'select ${SOURCE_MEMBER_KEY_COL_NAME}as member_key, null, null, 0 from ${SOURCE_TABLE_NAME} where ${SOURCE_PARENT_KEY_COL_NAME} is null' || chr(10)
|| 'union all' || chr(10)
|| 'select' || chr(10)
|| ' member_key,' || chr(10)
|| ' replace(replace(ancestor_key, ''\p'', ''|''), ''\'', ''\'') as ancestor_key,' || chr(10)
|| ' case when depth is null then 0' || chr(10)
|| ' else max(depth) over (partition by member_key) - depth + 1' || chr(10)
|| ' end as distance,' || chr(10)
|| ' is_leaf' || chr(10)
|| 'from' || chr(10)
|| '(' || chr(10)
|| ' select' || chr(10)
|| ' member_key,' || chr(10)
|| ' depth,' || chr(10)
|| ' case' || chr(10)
|| ' when depth is null then '''' || member_key' || chr(10)
|| ' when instr(hier_path, ''|'', 1, depth + 1) = 0 then null' || chr(10)
|| ' else substr(hier_path, instr(hier_path, ''|'', 1, depth) + 1, instr(hier_path, ''|'', 1, depth + 1) - instr(hier_path, ''|'', 1, depth) - 1)' || chr(10)
|| ' end ancestor_key,' || chr(10)
|| ' is_leaf' || chr(10)
|| ' from' || chr(10)
|| ' (' || chr(10)
|| ' select ${SOURCE_MEMBER_KEY_COL_NAME} as member_key, ${SOURCE_PARENT_KEY_COL_NAME} as ancestor_key, sys_connect_by_path(replace(replace(${SOURCE_MEMBER_KEY_COL_NAME}, ''\'', ''\''), ''|'', ''\p''), ''|'') as hier_path,' || chr(10)
|| ' case when ${SOURCE_MEMBER_KEY_COL_NAME} in (select ${SOURCE_PARENT_KEY_COL_NAME} from ${SOURCE_TABLE_NAME} ) then 0 else 1 end as IS_LEAF' || chr(10)
|| ' from ${SOURCE_TABLE_NAME} ' || chr(10)
|| ' connect by prior ${SOURCE_MEMBER_KEY_COL_NAME} = ${SOURCE_PARENT_KEY_COL_NAME} ' || chr(10)
|| ' start with ${SOURCE_PARENT_KEY_COL_NAME} is null' || chr(10)
|| ' ),' || chr(10)
|| ' (' || chr(10)
|| ' select null as depth from dual' || chr(10);
for i in 1..v_max_depth - 1 loop
v_stmt := v_stmt || ' union all select ' || i || ' from dual' || chr(10);
end loop;
v_stmt := v_stmt || ' )' || chr(10)
|| ')' || chr(10)
|| 'where ancestor_key is not null' || chr(10);
execute immediate v_stmt;
end;
/
Example 20-3 Employee Table Example
declare
v_max_depth integer;
v_stmt varchar2(32000);
i integer;
begin
select max(level) into v_max_depth
from SAMP_EMPL_D_VH
connect by prior Employee_Key=Mgr_Id
start with Mgr_Id is null;
v_stmt := 'insert into 07 - ORE Sample.<<SQLSERVER_TABLE>> (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)' || chr(10)
|| 'select Employee_Key as member_key, null, null, 0 from SAMP_EMPL_D_VH where Mgr_Id is null' || chr(10)
|| 'union all' || chr(10)
|| 'select' || chr(10)
|| ' member_key,' || chr(10)
|| ' replace(replace(ancestor_key, ''\p'', ''|''), ''\'', ''\'') as ancestor_key,' || chr(10)
|| ' case when depth is null then 0' || chr(10)
|| ' else max(depth) over (partition by member_key) - depth + 1' || chr(10)
|| ' end as distance,' || chr(10)
|| ' is_leaf' || chr(10)
|| 'from' || chr(10)
|| '(' || chr(10)
|| ' select' || chr(10)
|| ' member_key,' || chr(10)
|| ' depth,' || chr(10)
|| ' case' || chr(10)
|| ' when depth is null then '''' || member_key' || chr(10)
|| ' when instr(hier_path, ''|'', 1, depth + 1) = 0 then null' || chr(10)
|| ' else substr(hier_path, instr(hier_path, ''|'', 1, depth) + 1, instr(hier_path, ''|'', 1, depth + 1) - instr(hier_path, ''|'', 1, depth) - 1)' || chr(10)
|| ' end ancestor_key,' || chr(10)
|| ' is_leaf' || chr(10)
|| ' from' || chr(10)
|| ' (' || chr(10)
|| ' select Employee_Key as member_key, Mgr_Id as ancestor_key, sys_connect_by_path(replace(replace(Employee_Key, ''\'', ''\''), ''|'', ''\p''), ''|'') as hier_path,' || chr(10)
|| ' case when Employee_Key in (select Mgr_Id from SAMP_EMPL_D_VH ) then 0 else 1 end as IS_LEAF' || chr(10)
|| ' from SAMP_EMPL_D_VH ' || chr(10)
|| ' connect by prior Employee_Key = Mgr_Id ' || chr(10)
|| ' start with Mgr_Id is null' || chr(10)
|| ' ),' || chr(10)
|| ' (' || chr(10)
|| ' select null as depth from dual' || chr(10);
for i in 1..v_max_depth - 1 loop
v_stmt := v_stmt || ' union all select ' || i || ' from dual' || chr(10);
end loop;
v_stmt := v_stmt || ' )' || chr(10)
|| ')' || chr(10)
|| 'where ancestor_key is not null' || chr(10);
execute immediate v_stmt;
end;
/
SQL Server
Example 20-4 Create Table
CREATE TABLE ${PARENT_CHILD_TABLE_NAME} ( MEMBER_KEY ${MEMBER_TYPE_DEF} , ANCESTOR_KEY ${ANCESTOR_TYPE_DEF}, DISTANCE INTEGER, IS_LEAF INTEGER )
Example 20-5 Insert Content
WITH Closure(${SOURCE_PARENT_KEY_COL_NAME}, ${SOURCE_MEMBER_KEY_COL_NAME}, Distance) as (
select directs.${SOURCE_PARENT_KEY_COL_NAME}, directs.${SOURCE_MEMBER_KEY_COL_NAME}, NULL from ${SOURCE_TABLE_NAME} directs where directs.${SOURCE_PARENT_KEY_COL_NAME} is NULL union all
select directs.${SOURCE_MEMBER_KEY_COL_NAME} ,directs.${SOURCE_MEMBER_KEY_COL_NAME}, 0 from ${SOURCE_TABLE_NAME} directs union all
select super.${SOURCE_PARENT_KEY_COL_NAME}, sub.${SOURCE_MEMBER_KEY_COL_NAME}, sub.Distance+1 from ${SOURCE_TABLE_NAME} super, Closure sub
where super.${SOURCE_MEMBER_KEY_COL_NAME} = sub.${SOURCE_PARENT_KEY_COL_NAME} and super.${SOURCE_PARENT_KEY_COL_NAME} is not NULL)
Insert into ${PARENT_CHILD_TABLE_NAME} (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)
select Closure.${SOURCE_MEMBER_KEY_COL_NAME} as MEMBER_KEY, Closure.${SOURCE_PARENT_KEY_COL_NAME} as ANCESTOR_KEY, Closure.Distance as DISTANCE,
Case WHEN temp.${SOURCE_PARENT_KEY_COL_NAME} is null then 1 else 0 end as IS_LEAF
from Closure Left Outer Join (select distinct ${SOURCE_PARENT_KEY_COL_NAME} from ${SOURCE_TABLE_NAME} ) temp on Closure.${SOURCE_MEMBER_KEY_COL_NAME}= temp.${SOURCE_PARENT_KEY_COL_NAME} where Closure.${SOURCE_PARENT_KEY_COL_NAME} is NOT NULL or (Closure.${SOURCE_PARENT_KEY_COL_NAME} is NULL and Closure.Distance is NULL)
Example 20-6 Original File
WITH Closure(Mgr_Id, Employee_Key, Distance) as (
select directs.Mgr_Id, directs.Employee_Key, NULL from SAMP_EMPL_D_VH directs where directs.Mgr_Id is NULL union all
select directs.Employee_Key ,directs.Employee_Key, 0 from SAMP_EMPL_D_VH directs union all
select super.Mgr_Id, sub.Employee_Key, sub.Distance+1 from SAMP_EMPL_D_VH super, Closure sub
where super.Employee_Key = sub.Mgr_Id and super.Mgr_Id is not NULL)
Insert into 07 - ORE Sample.<<TABLE_NAME>> (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)
select Closure.Employee_Key as MEMBER_KEY, Closure.Mgr_Id as ANCESTOR_KEY, Closure.Distance as DISTANCE,
Case WHEN temp.Mgr_Id is null then 1 else 0 end as IS_LEAF
from Closure Left Outer Join (select distinct Mgr_Id from SAMP_EMPL_D_VH ) temp on Closure.Employee_Key = temp.Mgr_Id where Closure.Mgr_Id is NOT NULL or (Closure.Mgr_Id is NULL and Closure.Distance is NULL)
IBM DB2
Example 20-7 Create Table
CREATE TABLE "${PARENT_CHILD_TABLE_NAME} " ( "MEMBER_KEY" ${MEMBER_TYPE_DEF} , "ANCESTOR_KEY" ${ANCESTOR_TYPE_DEF}, "DISTANCE" INTEGER, "IS_LEAF" INTEGER )
Example 20-8 Insert Content
Insert into "${PARENT_CHILD_TABLE_NAME} " (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)
WITH Closure(${SOURCE_PARENT_KEY_COL_NAME}, ${SOURCE_MEMBER_KEY_COL_NAME}, Distance) as (
select directs.${SOURCE_PARENT_KEY_COL_NAME}, directs.${SOURCE_MEMBER_KEY_COL_NAME}, NULL from ${SOURCE_TABLE_NAME} directs where directs.${SOURCE_PARENT_KEY_COL_NAME} is NULL union all
select directs.${SOURCE_MEMBER_KEY_COL_NAME} ,directs.${SOURCE_MEMBER_KEY_COL_NAME}, 0 from ${SOURCE_TABLE_NAME} directs union all
select super.${SOURCE_PARENT_KEY_COL_NAME}, sub.${SOURCE_MEMBER_KEY_COL_NAME}, sub.Distance+1 from ${SOURCE_TABLE_NAME} super, Closure sub
where super.${SOURCE_MEMBER_KEY_COL_NAME} = sub.${SOURCE_PARENT_KEY_COL_NAME} and super.${SOURCE_PARENT_KEY_COL_NAME} is not NULL)
select Closure.${SOURCE_MEMBER_KEY_COL_NAME} as MEMBER_KEY, Closure.${SOURCE_PARENT_KEY_COL_NAME} as ANCESTOR_KEY, Closure.Distance as DISTANCE,
Case WHEN temp.${SOURCE_PARENT_KEY_COL_NAME} is null then 1 else 0 end as IS_LEAF
from Closure Left Outer Join (select distinct ${SOURCE_PARENT_KEY_COL_NAME} from ${SOURCE_TABLE_NAME} ) temp on Closure.${SOURCE_MEMBER_KEY_COL_NAME} = temp.${SOURCE_PARENT_KEY_COL_NAME} where Closure.${SOURCE_PARENT_KEY_COL_NAME} is NOT NULL or (Closure.${SOURCE_PARENT_KEY_COL_NAME} is NULL and Closure.Distance is NULL)
Example 20-9 Original File
Insert into "07 - ORE Sample"."<<CreateTable>>" (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)
WITH Closure(Mgr_Id, Employee_Key, Distance) as (
select directs.Mgr_Id, directs.Employee_Key, NULL from SAMP_EMPL_D_VH directs where directs.Mgr_Id is NULL union all
select directs.Employee_Key ,directs.Employee_Key, 0 from SAMP_EMPL_D_VH directs union all
select super.Mgr_Id, sub.Employee_Key, sub.Distance+1 from SAMP_EMPL_D_VH super, Closure sub
where super.Employee_Key = sub.Mgr_Id and super.Mgr_Id is not NULL)
select Closure.Employee_Key as MEMBER_KEY, Closure.Mgr_Id as ANCESTOR_KEY, Closure.Distance as DISTANCE,
Case WHEN temp.Mgr_Id is null then 1 else 0 end as IS_LEAF
from Closure Left Outer Join (select distinct Mgr_Id from SAMP_EMPL_D_VH ) temp on Closure.Employee_Key = temp.Mgr_Id where Closure.Mgr_Id is NOT NULL or (Closure.Mgr_Id is NULL and Closure.Distance is NULL)
Teradata
Example 20-10 Create Table
CREATE MULTISET TABLE "${PARENT_CHILD_TABLE_NAME} " ( "MEMBER_KEY" ${MEMBER_TYPE_DEF} , "ANCESTOR_KEY" ${ANCESTOR_TYPE_DEF}, "DISTANCE" INTEGER, "IS_LEAF" INTEGER )
Example 20-11 Insert Content
Insert into "${PARENT_CHILD_TABLE_NAME} " (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)
WITH RECURSIVE Closure(${SOURCE_PARENT_KEY_COL_NAME}, ${SOURCE_MEMBER_KEY_COL_NAME}, Distance) as (
select directs.${SOURCE_PARENT_KEY_COL_NAME}, directs.${SOURCE_MEMBER_KEY_COL_NAME}, NULL from ${SOURCE_TABLE_NAME} directs where directs.${SOURCE_PARENT_KEY_COL_NAME} is NULL union all
select directs.${SOURCE_MEMBER_KEY_COL_NAME} ,directs.${SOURCE_MEMBER_KEY_COL_NAME}, 0 from ${SOURCE_TABLE_NAME} directs union all
select super.${SOURCE_PARENT_KEY_COL_NAME}, sub.${SOURCE_MEMBER_KEY_COL_NAME}, sub.Distance+1 from ${SOURCE_TABLE_NAME} super, Closure sub
where super.${SOURCE_MEMBER_KEY_COL_NAME} = sub.${SOURCE_PARENT_KEY_COL_NAME} and super.${SOURCE_PARENT_KEY_COL_NAME} is not NULL)
select Closure.${SOURCE_MEMBER_KEY_COL_NAME} as MEMBER_KEY, Closure.${SOURCE_PARENT_KEY_COL_NAME} as ANCESTOR_KEY, Closure.Distance as DISTANCE,
Case WHEN temp.${SOURCE_PARENT_KEY_COL_NAME} is null then 1 else 0 end as IS_LEAF
from Closure Left Outer Join (select distinct ${SOURCE_PARENT_KEY_COL_NAME} from ${SOURCE_TABLE_NAME} ) temp on Closure.${SOURCE_MEMBER_KEY_COL_NAME} = temp.${SOURCE_PARENT_KEY_COL_NAME} where Closure.${SOURCE_PARENT_KEY_COL_NAME} is NOT NULL or (Closure.${SOURCE_PARENT_KEY_COL_NAME} is NULL and Closure.Distance is NULL)
Example 20-12 Original File
Insert into "07 - ORE Sample"."<<CREATE_TABLE>>" (MEMBER_KEY, ANCESTOR_KEY, DISTANCE, IS_LEAF)
WITH RECURSIVE Closure(Mgr_Id, Employee_Key, Distance) as (
select directs.Mgr_Id, directs.Employee_Key, NULL from SAMP_EMPL_D_VH directs where directs.Mgr_Id is NULL union all
select directs.Employee_Key ,directs.Employee_Key, 0 from SAMP_EMPL_D_VH directs union all
select super.Mgr_Id, sub.Employee_Key, sub.Distance+1 from SAMP_EMPL_D_VH super, Closure sub
where super.Employee_Key = sub.Mgr_Id and super.Mgr_Id is not NULL)
select Closure.Employee_Key as MEMBER_KEY, Closure.Mgr_Id as ANCESTOR_KEY, Closure.Distance as DISTANCE,
Case WHEN temp.Mgr_Id is null then 1 else 0 end as IS_LEAF
from Closure Left Outer Join (select distinct Mgr_Id from SAMP_EMPL_D_VH ) temp on Closure.Employee_Key = temp.Mgr_Id where Closure.Mgr_Id is NOT NULL or (Closure.Mgr_Id is NULL and Closure.Distance is NULL)