Honestly, your master table design needs to be normalized. But, in the meantime you could try this query below :
with
needed_rows_for_cat1_tab (lvl) as (
select level from dual
connect by level <= (select max(regexp_count(Cat1, ';')) from Your_bad_master_tab) + 1
)
, needed_rows_for_cat2_tab (lvl) as (
select level from dual
connect by level <= (select max(regexp_count(Cat2, ';')) from Your_bad_master_tab) + 1
)
, split_cat1_val_tab as (
select Entity, Cat1
, substr(Cat1||';'
, lag(pos, 1, 0)over(partition by Entity order by lvl) + 1
, pos - lag(pos, 1, 0)over(partition by Entity order by lvl) - 1
) val
, lvl
, pos
, 1 cat
from (
select Entity, Cat1, instr(Cat1||';', ';', 1, r1.lvl)pos, r1.lvl
from Your_bad_master_tab c1
join needed_rows_for_cat1_tab r1 on r1.lvl <= regexp_count(Cat1, ';') + 1
)
)
, split_cat2_val_tab as (
select Entity, Cat2
, substr(Cat2||';'
, lag(pos, 1, 0)over(partition by Entity order by lvl) + 1
, pos - lag(pos, 1, 0)over(partition by Entity order by lvl) - 1
) val
, lvl
, pos
, 2 cat
from (
select Entity, Cat2, instr(Cat2||';', ';', 1, r2.lvl)pos, r2.lvl
from Your_bad_master_tab c1
join needed_rows_for_cat2_tab r2 on r2.lvl <= regexp_count(Cat2, ';') + 1
)
)
select ENTITY
, max(decode(cat, 1, CAT1, null)) CAT1
, listagg(decode(cat, 1, EMAIL, null), ';')within group (order by lvl) EmailCat1
, max(decode(cat, 2, CAT1, null)) CAT2
, listagg(decode(cat, 2, EMAIL, null), ';')within group (order by lvl) EmailCat2
from (
select c.*, p.Email
from split_cat1_val_tab c join Your_person_tab p on (c.val = p.name)
union all
select c.*, p.Email
from split_cat2_val_tab c join Your_person_tab p on (c.val = p.name)
)
group by ENTITY
;
Here are your sample data
--drop table Your_bad_master_tab purge;
create table Your_bad_master_tab (Entity, Cat1, Cat2) as
select 'A', 'Mary;Steve', 'Jacob' from dual union all
select 'B', 'Alex;John', 'Sally;Andrew' from dual
;
--drop table Your_person_tab purge;
create table Your_person_tab (Name, Email, InfoID) as
select 'Mary', 'maryD@gmail.com' ,'mryD' from dual union all
select 'Steve', 'steveR@gmail.com' ,'stvR' from dual union all
select 'Jacob', 'jacobB@gmail.com' ,'jacbb' from dual union all
select 'Sally', 'sallyD@gmail.com' ,'sallD' from dual union all
select 'Alex', 'AlexT@gmail.com' ,'alexT' from dual union all
select 'John', 'JohnP@gmail.com' ,'johP' from dual union all
select 'Andrew', 'AndrewV@gmail.com' ,'andV' from dual
;
DB<>fiddle