Во-первых, создайте таблицы и данные:
create table mytable (id,name) as
(
select 1, 'string_1 string_2 string_3 string_N' from dual
union all
select 2, null from dual
union all
select 3, 'Ma Lo' from dual
);
Во-вторых, создайте необходимые нам типы объектов и таблиц:
CREATE OR REPLACE TYPE name_tbl AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE TYPE id_name_rec AS OBJECT ( id NUMBER, name VARCHAR2(100));
CREATE OR REPLACE TYPE id_name_tbl AS TABLE OF id_name_rec;
В-третьих, создайте функцию, которую мыЯ буду вызывать рекурсивно для анализа значений имен:
CREATE OR REPLACE
FUNCTION parse_name ( v_name IN VARCHAR2 )
RETURN name_tbl
IS
tbl name_tbl;
subtbl name_tbl;
subname VARCHAR2(100);
thisname VARCHAR2(100);
num INT := 1;
idx INT := 1;
idxspace INT := 0;
BEGIN
IF v_name IS NOT NULL THEN
tbl := name_tbl();
-- find the number of values
FOR ws IN 1 .. LENGTH(v_name) loop
IF ( substr(v_name,ws,1) = ' ' ) THEN
num := num + 1;
END IF;
END loop;
IF ( num > 1 ) THEN
-- increase tbl size
-- get the index of the first whitespace
idxspace := instr( v_name, ' ');
-- get thisname
thisname := substr( v_name, 1, idxspace-1 );
-- substring name and make recursive call;
subname := substr( v_name, idxspace+1 );
subtbl := parse_name (subname);
FOR i IN 1 .. subtbl.count()
loop
tbl.EXTEND;
-- add each subtbl value to tbl
tbl( tbl.count ) := subtbl(i);
tbl.EXTEND;
-- now prepend this name to each value of subtbl and add to tbl
tbl( tbl.count ) := thisname||' '||subtbl(i);
END loop;
ELSE
thisname := v_name;
END IF;
tbl.EXTEND;
tbl (tbl.count) := thisname;
END IF;
RETURN tbl;
exception
WHEN others THEN dbms_output.put_line('whoops: '||sqlerrm);
END;
В-четвертых, создайте основную функцию, которая возвращает рефкурсор:
create or replace
FUNCTION parse_mytable_name
RETURN sys_refcursor
IS
retcur sys_refcursor;
idnametbl id_name_tbl := id_name_tbl();
valuetbl name_tbl;
BEGIN
-- get all the records from mytable
FOR z IN ( SELECT ID, NAME FROM mytable )
loop
valuetbl := parse_name(z.NAME);
IF ( valuetbl IS NOT NULL ) THEN
FOR i IN 1 .. valuetbl.count
loop
idnametbl.EXTEND;
idnametbl( idnametbl.count ) := id_name_rec( z.ID, valuetbl(i));
END loop;
END IF;
END loop;
OPEN retcur FOR SELECT ID, NAME FROM TABLE (idnametbl) order by id, name;
RETURN retcur;
exception WHEN others THEN dbms_output.put_line('whoops in parse_mytable_name: '||sqlerrm);
END parse_mytable_name;
В-пятых, тестовый сценарий pl / sql:
DECLARE
retcur sys_refcursor;
testid INT;
testname varchar2(100);
BEGIN
retcur := parse_mytable_name();
loop
fetch retcur INTO testid,testname;
exit WHEN retcur%notfound;
dbms_output.put_line('testid: '||testid||', testname: '||testname);
END loop;
EXCEPTION
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('whoops: '||SQLERRM);
END;
Наконец, результаты теста:
testid: 1, testname: string_1
testid: 1, testname: string_1 string_2
testid: 1, testname: string_1 string_2 string_3
testid: 1, testname: string_1 string_2 string_3 string_N
testid: 1, testname: string_1 string_2 string_N
testid: 1, testname: string_1 string_3
testid: 1, testname: string_1 string_3 string_N
testid: 1, testname: string_1 string_N
testid: 1, testname: string_2
testid: 1, testname: string_2 string_3
testid: 1, testname: string_2 string_3 string_N
testid: 1, testname: string_2 string_N
testid: 1, testname: string_3
testid: 1, testname: string_3 string_N
testid: 1, testname: string_N
testid: 3, testname: Lo
testid: 3, testname: Ma
testid: 3, testname: Ma Lo