No result from a query stored in a VARCHAR2 variable is treated as an empty string or a NULL? - Oracle PL/SQL - Oracle 11g - oracle

I have a procedure which performs a SELECT INTO and stores the returned result in a VARCHAR2 variable called account_:
SELECT DISTINCT NVL(XYZ_API.Get_Ref(company, currency, pay_type, order_id),
XYZ_API.Get_Id(company, currency, pay_type, order_id)) account
INTO account_
FROM some_table
WHERE company = company_
AND payment_type = 'SUPP'
AND order_id = order_id_
AND payment_date = pay_date_;
company_, order_id_, and pay_date_ are all VARCHAR2 variables I am using to filter out the records.
The result taken into account_ is then formatted as follows and stored in another VARCHAR2 variable named giro_:
giro_ := LPAD( TRANSLATE( account_, '1234567890- ','1234567890' ), 16, '0' );
Then I check whether giro_ is a number, as follows with a FOR loop.
FOR i_ IN 1..LENGTH( giro_ ) LOOP
c_ := ASCII( SUBSTR( giro_ , i_, 1 ) );
IF ( c_ < ASCII( '0' ) OR c_ > ASCII( '9' ) ) THEN
RETURN FALSE;
END IF;
END LOOP;
I have a scenario where the SELECT query shown above does not pick up any records.
This eventually introduces an exception in the FOR loop with errors ORA-06502 and ORA-06512.
As per my understanding the cause is LENGTH( giro_ ).
1..LENGTH( giro_ ) is failing as the LENGTH of giro_ value (LENGTH being a NULL) cannot be converted into a NUMBER.
My question is, by this time in this scenario, is giro_ an empty string or a NULL?
What exactly happens here?
Thanks!

Your understanding is wrong. LENGTH will always return a number (string length), it doesn't check whether its argument is a number.
Exception from the rule is if its (LENGTH's) argument is an empty string (in Oracle, it is equal to NULL) - then the length is also unknown (NULL):
SQL> select length(''), length(null) from dual;
LENGTH('') LENGTH(NULL)
---------- ------------
SQL>
In that case, modify your code so that it includes the NVL function, e.g.
FOR i_ IN 1 .. NVL(LENGTH( giro_ ), 0) LOOP
For empty strings, it won't do anything.

Related

order by of nested table not taking variable value

SELECT CAST(MULTISET(SELECT *
FROM TABLE(table_a)
ORDER BY loc_sort_column DESC
) as table_a_type
)
INTO table_b
FROM dual;
I have this statement that tosses 1 collection of data into another nested table after sorting it.
The problem I am having is that it is not sorting. I have a variable loc_sort_column that in this case will be a integer/number between 1 and 11 selecting the column but it is not working.
I have stuck 1 in there and it works fine but it doesn't seem to like the variable that contains 1.
Is this an order of operation or something?
What you are doing is ordering by a constant. Following AskTom:
you cannot dynamically return a column position, you dynamically
returned A CONSTANT, A NUMBER, A CONSTANT VALUE - JUST A VALUE
order by < ordinal position> | < expression>
you are returning an expression - not an ordinal position.
What you can do is to use DECODE function to decode column position to real column. Please check my code sample:
CREATE OR REPLACE TYPE my_type FORCE AS OBJECT
(
col_1 NUMBER
,col_2 NUMBER
);
/
CREATE OR REPLACE TYPE my_nested_table AS TABLE of my_type;
/
DECLARE
l_table_a my_nested_table := my_nested_table();
l_table_b my_nested_table := my_nested_table();
l_sort_column NUMBER := 2;
BEGIN
l_table_a.extend(100);
FOR l_i IN 1..l_table_a.COUNT LOOP
l_table_a(l_i) := my_type(ROUND(DBMS_RANDOM.RANDOM,0), ROUND(DBMS_RANDOM.RANDOM,0));
END LOOP;
SELECT CAST(
MULTISET(
SELECT *
FROM TABLE(l_table_a)
ORDER BY DECODE(l_sort_column, 1, col_1
, 2, col_2) DESC
) AS my_nested_table
)
INTO l_table_b
FROM dual;
DBMS_OUTPUT.PUT_LINE('col_1 col_2');
FOR l_i IN 1..l_table_b.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(l_table_b(l_i).col_1 || ' ' || l_table_b(l_i).col_2);
END LOOP;
END;
/

Extract TEXT from a CLOB field

I have a CLOB field in my Oracle Database that store TEXT data in the following format:
__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;
I'm using TOAD and while I am creating the query I can read the CLOB field with the following:
--- To read the CLOB field.
select DBMS_LOB.substr(ADD_INFO_MASTER) from USER
This select return me the CLOB field HUMAN READABLE.
My question is: Is there any way to extract the one single value like ACCOUNT value from the line above?
Keep in mind that this CLOB field can variate and the __17__ACCOUNT= will not be in the same place every time. I need a way to extract to locate the ;;__17__ACCOUNT= (this will be a pattern) and extract the the value 37004968.
It is possible to achieve this while performing a query in TOAD?
If you want to deal with CLOB values larger than 4000 symbols length (Oracle 11g) or 32K length (Oracle 12c) then you must use DBMS_LOB package.
This package contains instr() and substr() functions which operates on LOBs.
In your case query looks like that:
with prm as (
select '__17__ACCOUNT' as fld_start from dual
)
select
dbms_lob.substr(
text,
-- length of substring
(
-- position of delimiter found after start of desired field
dbms_lob.instr(text, ';;', dbms_lob.instr(text, prm.fld_start))
-
-- position of the field description plus it's length
( dbms_lob.instr(text, prm.fld_start) + length(fld_start) + 1 )
),
-- start position of substring
dbms_lob.instr(text,prm.fld_start) + length(fld_start) + 1
)
from
text_table,
prm
Query above uses this setup:
create table text_table(text clob);
insert into text_table(text) values (
'__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;'
);
For everyday use with development tools it may be useful to define a function which returns value of field with desired name and use it instead of writing complicated expressions each time.
E.g. :
create or replace function get_field_from_text(
pi_text in clob,
pi_field_name in varchar2
) return varchar2 deterministic parallel_enable
is
v_start_pos binary_integer;
v_field_start varchar2(4000);
v_field_value varchar2(32767);
begin
if( (pi_text is null) or (pi_field_name is null) ) then
return null;
end if;
v_field_start := pi_field_name || '=';
v_start_pos := dbms_lob.instr(pi_text, v_field_start);
if(v_start_pos is null) then
return null;
end if;
v_start_pos := v_start_pos + length(v_field_start);
v_field_value := dbms_lob.substr(
pi_text,
(dbms_lob.instr(pi_text, ';;', v_start_pos) - v_start_pos),
v_start_pos
);
return v_field_value;
end;
Usage:
select get_field_from_text(text,'__17__OUTPUT_DEVICE_46') from text_table
You could use a regular expression to extract the value:
WITH your_table AS (
SELECT '__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;' clob_field FROM DUAL
)
SELECT REGEXP_SUBSTR(clob_field,'__17__ACCOUNT=.*;;')
FROM your_table
Using that you would get "__17__ACCOUNT=37004968;;". You can easily extract the value with SUBSTR.
I think that in Oracle 11g REGEXP_SUBSTR has extra parameters that would let you extract a certain group within the regular expression.
You can use INSTR and SUBSTR with CLOB datatype:
WITH T1 AS (
SELECT '__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;' TEXT FROM DUAL
)
SELECT SUBSTR(TEXT,
INSTR(TEXT, '__17__ACCOUNT=') + LENGTH('__17__ACCOUNT') + 1, -- find the first position of the value
INSTR (TEXT, ';;', INSTR(TEXT, '__17__ACCOUNT=')) - (INSTR(TEXT, '__17__ACCOUNT=') + LENGTH('__17__ACCOUNT') + 1) -- length to read. Difference between the end position (the first ;; after your placeholder) and the value start position (the same value as above)
)
FROM T1;
However I like the REGEXP solution proposed by pablomatico more.

Updating table in SQLPLUS (Stored Procedure Loop with Comma Delimited Column)

Having some trouble writing my stored procedure. Using Oracle 11g
Goal: I want to be able to create separate rows in my table "info_table" from my table "places_table" with the column alternatenames. Under the column alternatenames from places_table, there is a comma delimited string with multiple alternate names. I want to create a row for each one of these alternate names in table "info_table".
ex of alternatenames column string:
Beijing,Beijingzi,Pei-ching-tzu
what I am hoping to achieve
ID Name
100000000 Beijing
100000001 Beijingzi
100000002 Pei-ching-tzu
Currently my code looks like this:
CREATE TABLE INFO_TABLE
(
INFOID NUMBER PRIMARY KEY,
NAME VARCHAR2(500),
LANGUAGE VARCHAR2(40),
STATUS VARCHAR2(50),
COUNTRY_CODE CHAR (10),
COUNTRY_CODE_2 CHAR (10),
GID CHAR(10),
SUPPLIERID CHAR(10),
LAST_MODIFIED CHAR(50)
);
CREATE SEQUENCE INFO_COUNTER
START WITH 100000000;
CREATE PROCEDURE LOAD_ALTERNATE_NAMES(ALTERNATENAMES_COLUMN VARCHAR2)
AS
COMMA_FINDER NUMBER := 1;
BEGIN
IF ALTERNATENAMES_COLUMN IS NOT NULL
THEN
<<SEPARATE_ALTERNATENAMES>> WHILE COMMA_FINDER!=0 LOOP
INSERT INTO INFO_TABLE
(INFOID, NAME, LANGUAGE, STATUS, COUNTRY_CODE, COUNTRY_CODE_2, GID, SUPPLIERID, LAST_MODIFIED)
VALUES
(INFO_COUNTER, SUBSTR(ALTERNATENAMES_COLUMN, INSTR(P.ALTERNATENAMES, ',', COMMA_FINDER+1)), NULL, 'ALTERNATE', P.COUNTRY_CODE, P.COUNTRY_CODE_2, P.GID, NULL, P.LASTMODIFIED)
FROM INFO_TABLE I, PLACES_TABLE P;
COMMA_FINDER := INSTR(ALTERNATENAMES, ',', COMMA_FINDER);
END LOOP SEPARATE_ALTERNATENAMES;
COMMA_FINDER:=1;
ENDIF;
END
/
LOAD_ALTERNATE_NAMES(SELECT ALTERNATENAMES FROM PLACES_TABLE);
currently the problem is that my INSERT statement in my loop is giving me "SQL Statement Ignored" and I am not sure why. I have taken a look at the stored procedure and loop documentation but can't figure out if I am doing something wrong or there is a typo.
can someone help me please?
Thank you in advance,
Norman
The INSERT statement has either the form:
INSERT INTO table (...) VALUES (...)
or:
INSERT INTO table (...) SELECT ... FROM ...
That's why Oracle issues an error message.
But there's more. You pass the ALTERNATENAMES string value to the stored procedure but need more data from the PLACES_TABLE. Furthermore, Oracle doesn't support stored procedure calls like this:
LOAD_ALTERNATE_NAMES(SELECT ALTERNATENAMES FROM PLACES_TABLE);
So I propose you create a stored procedure without parameters:
CREATE PROCEDURE LOAD_ALTERNATE_NAMES
AS
COMMA_FINDER NUMBER;
BEGIN
FOR REC IN (
SELECT * FROM PLACES_TABLE WHERE ALTERNATENAMES IS NOT NULL
) LOOP
COMMA_FINDER NUMBER := 1;
<<SEPARATE_ALTERNATENAMES>> WHILE COMMA_FINDER!=0 LOOP
INSERT INTO INFO_TABLE
(INFOID, NAME, LANGUAGE, STATUS, COUNTRY_CODE, COUNTRY_CODE_2, GID, SUPPLIERID, LAST_MODIFIED)
VALUES
(INFO_COUNTER.NEXTVAL, SUBSTR(REC.ALTERNATENAMES, INSTR(REC.ALTERNATENAMES, ',', COMMA_FINDER+1)), NULL, 'ALTERNATE', REC.COUNTRY_CODE, REC.COUNTRY_CODE_2, REC.GID, NULL, REC.LASTMODIFIED);
COMMA_FINDER := INSTR(REC.ALTERNATENAMES, ',', COMMA_FINDER);
END LOOP SEPARATE_ALTERNATENAMES;
END LOOP;
END
/
I hope that helps you proceed. I haven't test it and I'm afraid that SUBSTR will fail once it reaches the last name. But you'll figure that out.
Here is a little function I use to loop things like you are asking for. You can specify a delimiter.
The type...
type split_array is table of varchar2(32767) index by binary_integer;
The function...
function split(string_in varchar2, delim_in varchar2) return split_array is
i number :=0;
pos number :=0;
lv_str varchar2(32767) := string_in;
strings split_array;
dl number;
begin
-- determine first chuck of string
pos := instr(lv_str,delim_in,1,1);
-- get the length of the delimiter
dl := length(delim_in);
if (pos = 0) then --then we assume there is only 1 items in the list. so we just add the delimiter to the end which would make the pos length+1;
strings(1) := lv_str;
end if;
-- while there are chunks left, loop
while ( pos != 0) loop
-- increment counter
i := i + 1;
-- create array element for chuck of string
strings(i) := substr(lv_str,1,pos-1);
-- remove chunk from string
lv_str := substr(lv_str,pos+dl,length(lv_str));
-- determine next chunk
pos := instr(lv_str,delim_in,1,1);
-- no last chunk, add to array
if pos = 0 then
strings(i+1) := lv_str;
end if;
end loop;
-- return array
return strings;
end split;
How to use it...
declare
/* alternatenames varchar2(32767) := 'one,two,three,four'; */
nameArray split_array;
begin
for c1 in ( select alternatenames from yourTable where alternatenames is not null )
loop
nameArray := split(c1.alternatenames,',');
for i in 1..nameArray.count loop
/* dbms_output.put_line(nameArray(i)); */
insert into yourTable ( yourColumn ) values ( nameArray(i) );
end loop;
end loop;
end;
/

Shortname function not working properly

There is this code for generatig unique shortname from a table MMSTREPHDR .
I have shortnames kv,kv1,kv2,kv3 already in MMSTREPHDR . But on passing parameter kv it gives me kv1 and not kv4(since it's in LOOP) . Can't figure out what's wrong ?
FUNCTION FUN_GENERATE_SNAME (p_name VARCHAR2)
RETURN VARCHAR2
IS
vl_sname VARCHAR2 (15);
n_cnt NUMBER := 1;
vl_sub NUMBER;
CURSOR c1 (vl_sname VARCHAR2)
IS
SELECT a.repsname, a.repcode
FROM MMSTREPHDR a
WHERE TRIM (UPPER (a.repsname)) = TRIM (UPPER (vl_sname));
BEGIN
vl_sname := TRIM (SUBSTR (p_name, 1, 15));
FOR i IN c1 (vl_sname)
LOOP
vl_sub := LENGTH (TO_CHAR (n_cnt));
vl_sname := SUBSTR (vl_sname, 1, (15 - vl_sub)) || n_cnt;
n_cnt := n_cnt + 1;
END LOOP;
RETURN vl_sname;
EXCEPTION
WHEN OTHERS
THEN
RETURN vl_sname;
END fun_generate_sname;
Your starting parameter is 'kv'. This is what you pass to the cursor. Consequently your cursor will select one row , the row where MMSTREPHD.repsname = 'kv'.
So your loop logic will be executed once. So cnt = . Hencevl_sname` becomes 'kv1', which is the value you get when the loop exits cleanly.
The cleanest way to fix this would be to admit that MMSTREPHD.repsname is a smart key, consisting of two elements: a subsystem name and a report number. Splitting the column into two columns would make it a cinch to find the next report number for a given sub-system. You can even retain the composite value as a virtual column (11g or later), or maintain it with triggers which sucks a bit.
Otherwise:
select concat(p_name
, trim(to_char(max(to_number(nvl(replace(repsname,p_name),'0')))+1)) )
into vl_sname
from MMSTREPHD
where repsname like p_name||'%'
Caveat - I haven't tested this (yet) so ths brackets may not pair up correctly.
You are concatenating the in below statement as
vl_sname := SUBSTR (vl_sname, 1, (15 - vl_sub)) || n_cnt;
here n_cnt is given initial value to it as 1 in code above thats why its fetching you value as KV1.You should keep it null and after statement should increment it by 1 in loop. Hope will be usefull
try this function
function FUN_GENERATE_SNAME(p_name varchar2) return varchar2 is
l_idx number;
l_name_ln := length(trim(p_name));
begin
select max(substr(trim(UPPER(a.repsname)), 1, -length(trim(UPPER(a.repsname)) + l_name_ln))
into l_idx
from MMSTREPHDR a
where substr(trim(UPPER(a.repsname)), 1, l_name_ln) = trim(UPPER(vl_sname));
if l_idx is null then
-- mean name is unique
return vl_sname;
else
return vl_sname ||(l_idx + 1);
end if;
exception
when others then
return vl_sname;
end fun_generate_sname;

oracle plsql aggregate error: ORA-06502: character string buffer too small

i wrote a simple aggregate function that should work on columns with csv text in them by aggregating distinct values in a resulting csv string. the functions seems to work all the way to the end when it craps out with the ORA-06502 error right when it should be returning the result.
here is the code:
type def:
create or replace type array_union_typ as object (
union_agg nvarchar2(1000),
static function ODCIAggregateInitialize(sctx in out array_union_typ)
return number,
member function ODCIAggregateIterate (self in out array_union_typ,
value in nvarchar2)
return number,
member function ODCIAggregateTerminate (self in array_union_typ,
return_value out nvarchar2,
flags in number)
return number,
member function ODCIAggregateMerge(self in out array_union_typ,
ctx2 in array_union_typ)
return number,
static function agg_union(arg1 in nvarchar2,
arg2 in nvarchar2)
return nvarchar2
);
the body:
create or replace type body array_union_typ is
static function ODCIAggregateInitialize(sctx in out array_union_typ
) return number is
begin
sctx := array_union_typ(null);
return ODCIConst.Success;
end;
member function ODCIAggregateIterate(self in out array_union_typ,
value in nvarchar2
) return number is
begin
union_agg := array_union_typ.agg_union(union_agg, value);
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(self in array_union_typ,
return_value out nvarchar2,
flags in number
) return number is
begin
dbms_output.put_line('result: '''|| union_agg || ''', length:' || length(union_agg)); --this still prints
return_value := self.union_agg; -- <-- this is where the error is indicated
--return_value := 'x'; -- returning this still gives the error.
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(self in out array_union_typ,
ctx2 in array_union_typ
) return number is
begin
union_agg := array_union_typ.agg_union(union_agg, ctx2.union_agg);
return ODCIConst.Success;
end;
static function agg_union(arg1 in nvarchar2,
arg2 in nvarchar2)
return nvarchar2 is
result nvarchar2(1000);
orig nvarchar2(1000);
begin
dbms_output.enable;
orig := replace(arg1||','||arg2, chr(0));
FOR rec IN (SELECT DISTINCT(regexp_substr(orig, '[^,]+', 1, level)) AS a
FROM dual CONNECT BY regexp_substr(orig, '[^,]+', 1, level) IS NOT NULL ORDER BY a) LOOP
IF result IS NOT NULL THEN
result := result || ',' || rec.a;
ELSE
result := rec.a;
END IF;
END LOOP;
--dbms_output.put_line('endwith: ''' || result || '''');
RETURN substr(result,1,1000);
end;
end;
here is a test table and data:
SQL> desc uniontest
Name Null? Type
----------------------------------------- -------- ----------------------------
I NVARCHAR2(50)
SQL> select * from uniontest;
I
--------------------------------------------------
a
a
b,c
b,d,e
and finally, this is what happens if i try to use the aggregate function:
SQL> select array_union(i) from uniontest;
select array_union(i) from uniontest
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "M35456.ARRAY_UNION_TYP", line 25
result: 'a,b,c,d,e', length:9
if i simply pass a single character string like 'x' in the offending line, i still get the same error. only on a null result does it go away. i am stumped and out of ideas.
thanks for any help.
btw, if anyone has any idea why i get added \0 characters i my agg_union function parameters, i am dying to know about that, too.
The ODCIaggregate methods don't seem to be playing nicely with nvarchar2; you don't appear to be doing anything wrong since it works fine if those are all changed to varchar2.
If you are on 11gR2 and thus have the listagg function available - but currently aren't using that because you need to figure out the distinct values - you can combine it with your existing regexp_substr function, so you don't need your own type/function:
SELECT REPLACE(LISTAGG(a, ',') WITHIN GROUP (ORDER BY a), chr(0), '')
FROM (
SELECT DISTINCT(regexp_substr(i, '[^,]+', 1, level)) AS a
FROM uniontest CONNECT BY regexp_substr(i, '[^,]+', 1, level) IS NOT NULL
ORDER BY a
);
... which with your data gives:
REPLACE(LISTAGG(A,',')WITHINGROUP(ORDERBYA),CHR(0),'')
------------------------------------------------------
a,b,c,d,e
In earlier versions you can use SELECT REPLACE(WM_CONCAT(a), chr(0), '') instead.
(The chr(0) here and the \0 in your question seem to be related to nvarchar2, but someone else will need to chip in on the details...)
If I'm not mistaken this
SELECT DISTINCT(regexp_substr(orig, '[^,]+', 1, level)) AS a
FROM dual CONNECT BY regexp_substr(orig, '[^,]+', 1, level) IS NOT NULL ORDER BY a)
results in an infinite number of rows, since the CONNECT BY condition is not dependend on the row.
Since you loop over the result appending characters to a string sooner the string becomes to large.

Resources