'Oracle string separated by commas to multiple rows in a table

I have a lookup table as follows:

id descr
1   maths
2   reading
3   science
4   history
5   chemistry
6   physics

I have another table, cases, with the following data

cases
id  name    subjects    
1   ABC     maths,reading
2   DEF     maths, chemistry
3   GHI     physics,chemistry

I have a stored procedure that opens a cursor on the cases table and iterates over each case and inserts the value into the base table and child tables. I have a cases_subjects table which needs entry as follows:

caseId  Subject
1       1   
1       2
2       1
2       5
3       6
3       5

I have a stored procedure like below. It has so many other validations so using SP is the only option. What is the best way to get this done with the below code?

create or replace PROCEDURE INSERT_CASES AS 
v_subj number;
cursor c1 is select * from  cases;
begin
       for t_case in c1
        if t_case.subjects is not null then
            --use INSTR or call function to convert one row from cases to many rows in cases_subjects
        else
            --do nothing
        end if;
       end loop;       
       commit;
exception
when others then 
    log_error(0,  'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
    commit;
end;

Any input is highly appreciated.



Solution 1:[1]

You do not need to split the string and can join the table on delimited sub-string matches using LIKE:

SELECT c.id AS caseid,
       l.id AS subjectid
FROM   cases c
       INNER JOIN lookup l
       ON (','|| c.subjects || ',' LIKE '%,' || l.descr || ',%')

Which, for the sample data:

CREATE TABLE lookup (id, descr) AS
SELECT 1, 'maths'     FROM DUAL UNION ALL
SELECT 2, 'reading'   FROM DUAL UNION ALL
SELECT 3, 'science'   FROM DUAL UNION ALL
SELECT 4, 'history'   FROM DUAL UNION ALL
SELECT 5, 'chemistry' FROM DUAL UNION ALL
SELECT 6, 'physics'   FROM DUAL;

CREATE TABLE cases (id, name, subjects) AS
SELECT 1, 'ABC', 'maths,reading'     FROM DUAL UNION ALL
SELECT 2, 'DEF', 'maths,chemistry'  FROM DUAL UNION ALL
SELECT 3, 'GHI', 'physics,chemistry' FROM DUAL;

CREATE TABLE case_subjects (caseid NUMBER, subjectid NUMBER);

Outputs:

CASEID SUBJECTID
1 1
1 2
2 1
2 5
3 5
3 6

If you want it as an insert then you do not need a cursor and can use INSERT ... SELECT:

INSERT INTO case_subjects(caseid, subjectid)
SELECT c.id,
       l.id
FROM   cases c
       INNER JOIN lookup l
       ON (','|| c.subjects || ',' LIKE '%,' || l.descr || ',%')

db<>fiddle here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1