Replacing text in a BLOB Column - sql

In one of our tables we have a HUGEBLOB Column (Column name is DYNAMIC_DATA) which holding an XML data. What I need to do is updating a certain part of the text from within this BLOB.
I've tried this query:
UPDATE ape1_item_version
SET DYNAMIC_DATA = REPLACE (DYNAMIC_DATA,'Single period','Single period period set1')
WHERE name = 'PRIT ALL POOL for Duration Telephony 10_NA_G_V_H_N_Z2'
But I get the following error:
ORA-00932: inconsistent datatypes: expected NUMBER got BLOB
How can I execute REPLACE on the BLOB ?

REPLACE works on the following datatypes:
Both search_string and replacement_string, as well as char, can be any of the data types CHAR, VARCHAR2, NCHAR, NVARCHAR2, CLOB, or NCLOB.
You have chosen to store character data as a collection of bytes (BLOB). These can not be worked on directly because a BLOB has no context and is only a very very big number. It can't be converted to characters without your input: you need its character set to convert binary data to text.
You'll have to either code the function REPLACE yourself (using DBMS_LOB.instr for instance) or convert your data to a workable CLOB and use standard functions on the CLOB.
I would advise strongly to change the datatype of your column. This will prevent any further character set conversion error you will likely run into in the future.
If you really want to work with blobs, use functions like these:
SQL> CREATE OR REPLACE FUNCTION convert_to_clob(l_blob BLOB) RETURN CLOB IS
2 l_clob CLOB;
3 l_dest_offset NUMBER := 1;
4 l_src_offset NUMBER := 1;
5 l_lang_context NUMBER := dbms_lob.default_lang_ctx;
6 l_warning NUMBER;
7 BEGIN
8 dbms_lob.createtemporary(l_clob, TRUE);
9 dbms_lob.converttoclob(dest_lob => l_clob,
10 src_blob => l_blob,
11 amount => dbms_lob.lobmaxsize,
12 dest_offset => l_dest_offset,
13 src_offset => l_src_offset,
14 blob_csid => nls_charset_id('AL32UTF8'),
15 lang_context => l_lang_context,
16 warning => l_warning);
17 RETURN l_clob;
18 END convert_to_clob;
19 /
Function created
SQL> CREATE OR REPLACE FUNCTION convert_to_blob(l_clob CLOB) RETURN BLOB IS
2 l_blob BLOB;
3 l_dest_offset NUMBER := 1;
4 l_src_offset NUMBER := 1;
5 l_lang_context NUMBER := dbms_lob.default_lang_ctx;
6 l_warning NUMBER;
7 BEGIN
8 dbms_lob.createtemporary(l_blob, TRUE);
9 dbms_lob.converttoblob(dest_lob => l_blob,
10 src_clob => l_clob,
11 amount => dbms_lob.lobmaxsize,
12 dest_offset => l_dest_offset,
13 src_offset => l_src_offset,
14 blob_csid => nls_charset_id('AL32UTF8'),
15 lang_context => l_lang_context,
16 warning => l_warning);
17 RETURN l_blob;
18 END convert_to_blob;
19 /
Function created
You can call these functions directly from SQL:
SQL> UPDATE ape1_item_version
2 SET DYNAMIC_DATA = convert_to_blob(
3 REPLACE(convert_to_clob(DYNAMIC_DATA),
4 'Single period',
5 'Single period period set1')
6 )
7 WHERE NAME = 'PRIT ALL POOL for Duration Telephony 10_NA_G_V_H_N_Z2';
1 row updated

We can use something like the below query also with Oracle 11 and above if the blob object is of text.
`UPDATE table_name
SET text_blob_column-name = UTL_RAW.CAST_TO_RAW(
REPLACE(UTL_RAW.CAST_TO_VARCHAR2(text_blob_column-name),
'<existing value>',
'<value to update>')
)
WHERE where_clause_Column-name='171';`

Related

How to insert BLOB into Oracle table via SQL

I wanted to inser below as a BLOB into Oracle table
I looked at this solution as well.Sloution: But problem is pasting these chars in the SQL show ��
INSERT INTO JOB_DETAILS (JOB_DATA) VALUES (hextoraw('��'))
It's not the simple, because those are characters not the hex representation of the characters. For example
SQL> host cat data.txt
Hello
SQL> host od -x data.txt
0000000 6548 6c6c 206f 0a0d
0000010
So the hex for Hello appears to be "6548 6c6c 206f 0a0d"
But on this platform (windows) we byte swap data, and have a couple characters to terminate in the end of line. So when I ditch the last two (0a0d) and load it:
SQL> create table t ( b blob);
Table created.
SQL> insert into t values ( hextoraw('65486c6c206f'));
1 row created.
SQL> select utl_raw.cast_to_varchar2(b) from t;
UTL_RAW.CAST_TO_VARCHAR2(B)
-------------------------------------------------------------
eHll o
You can see that the bytes have been swapped. I need to take that into acount
SQL> delete t;
1 row deleted.
SQL> insert into t values ( hextoraw('48656c6c6f20'));
1 row created.
SQL> select utl_raw.cast_to_varchar2(b) from t;
UTL_RAW.CAST_TO_VARCHAR2(B)
-------------------------------------------------------------
Hello
But in reality, you'd rarely add a blob by using hex strings. Presumably your data is stored in a file. In which case, you would load it using DBMS_LOB.LOADBLOBFROMFILE, eg
SQL> set timing on
SQL> declare
2 l_bfile bfile;
3 l_blob blob;
4
5 l_dest_offset integer := 1;
6 l_src_offset integer := 1;
7 begin
8 insert into test_blob_bas (id, blob_data)
9 values (1, empty_blob())
10 return blob_data into l_blob;
11
12 l_bfile := bfilename('TEMP', 'myfile.dat');
13 dbms_lob.fileopen(l_bfile, dbms_lob.file_readonly);
14 dbms_lob.loadblobfromfile (
15 dest_lob => l_blob,
16 src_bfile => l_bfile,
17 amount => dbms_lob.lobmaxsize,
18 dest_offset => l_dest_offset,
19 src_offset => l_src_offset);
20 dbms_lob.fileclose(l_bfile);
21
22 commit;
23 end;
24 /
PL/SQL procedure successfully completed.

IF condition containing IN operator inside ORACLE Trigger is not working

I have a trigger that uses IF condition with IN operator and two variables v_audit_user and v_evdnse_user inside IN. Both variables are containing comma separated ID values. The trigger gets compiled successfully with no errors. I am not understanding why the IF condition with IN is not working. When I select the function that assigns value to the variables independently, I do see the comma separated values, so nothing is wrong with function (see screenshot).
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
user_audit kdd_review_owner.OWNER_ID%TYPE; /* The user that is displayed in audit */
user_evdnse kdd_review_owner.OWNER_ID%TYPE; /* The user that took action in evidence tab */
v_audit_user NUMBER; /* The HRCO/QA user from audit tab */
v_evdnse_user NUMBER; /* The HRCO/QA user from evidence tab */
LV_ERRORCODE VARCHAR2(1000);
BEGIN
/* pass the username into the variables */
SELECT kro.OWNER_ID into user_audit from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
SELECT kro.OWNER_ID into user_evdnse from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
/* fetch the comma separated IDs */
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_audit,'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE');
-- select ENTITY_ID into v_evdnse_user from table(f_get_arg_table(F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE')));
/* If the action taken is by QA or HRCO role */
IF (:NEW.ACTION_SEQ_ID in (v_audit_user,v_evdnse_user))
THEN
/* then insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as Y */
Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y');
-- ELSE
--
-- /* else the logged in user is NOT HRCO/QA hence insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as N */
--
-- Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
-- values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'N');
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
You cannot pass a comma-delimited string stored in a single variable to an IN condition and expect it to be parsed as multiple values as it is not.
If you want to use a single variable containing a delimited list then you will need to use string functions to find a sub-string match:
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
v_owner_id kdd_review_owner.OWNER_ID%TYPE;
v_audit_user VARCHAR2(1000);
v_evdnse_user VARCHAR2(1000);
LV_ERRORCODE VARCHAR2(1000);
BEGIN
SELECT OWNER_ID
into v_owner_id -- You only need one variable here
from kdd_review_owner
where OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'EVDNSE');
IF ','||v_audit_user||','||v_evdnse_user||',' LIKE '%,'||:NEW.ACTION_SEQ_ID||',%'
THEN
Insert into SC_HRCQA_CASE_ACTIONS (
ACTION_SEQ_ID, ACTION_BY_ID, ACTION_TS, STATUS_CD, CASE_INTRL_ID,
ACTION_ID, NEW_CASE_OWNR_ASSGN_ID, CASE_DUE_TS, PREV_CASE_OWNR_ASSGN_ID, IS_HRCO_QA
) values (
:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID,
:NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y'
);
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
/
I don't have your tables so I'll try to illustrate it using my own code.
This is what you're doing now: to us (humans), it is obvious that L_NEW_ID (its value is 10) is contained in L_VAR1 (its value is '10,20'). However, Oracle doesn't recognize that and returns "Not OK":
SQL> declare
2 -- does L_NEW_ID exist in L_VAR1 and L_VAR2 (using your code)?
3 l_new_id number := 10;
4 --
5 l_var1 varchar2(20) := '10,20';
6 l_var2 varchar2(20) := '30,40';
7 begin
8 if l_new_id in (l_var1, l_var2) then
9 dbms_output.put_line('OK');
10 else
11 dbms_output.put_line('Not OK');
12 end if;
13 end;
14 /
Not OK
PL/SQL procedure successfully completed.
SQL>
So, what to do? One option is to split variables to rows and then check whether search value (10, right?) exists in such a list of values (rows). The result is - as you can see - "OK":
SQL> declare
2 l_new_id number := 10;
3 --
4 l_var1 varchar2(20) := '10,20';
5 l_var2 varchar2(20) := '30,40';
6 l_cnt number;
7 begin
8 -- count how many times L_NEW_ID exists in L_VAR1 nad L_VAR2
9 select count(*)
10 into l_cnt
11 from (-- split L_VAR1 into rows
12 select regexp_substr(l_var1, '[^,]+', 1, level) val
13 from dual
14 connect by level <= regexp_count(l_var1, ',') + 1
15 union all
16 -- split L_VAR2 into rows
17 select regexp_substr(l_var2, '[^,]+', 1, level)
18 from dual
19 connect by level <= regexp_count(l_var2, ',') + 1
20 ) x
21 where x.val = l_new_id;
22
23 if l_cnt > 0 then
24 dbms_output.put_line('OK');
25 else
26 dbms_output.put_line('Not OK');
27 end if;
28 end;
29 /
OK
PL/SQL procedure successfully completed.
SQL>
Just to verify it, let's change L_NEW_ID value to e.g. 99 and see what happens; as expected, "Not OK" as 99 isn't contained in 10, 20 nor 30, 40.
SQL> l2
2* l_new_id number := 10;
SQL> c/10/99
2* l_new_id number := 99;
SQL> /
Not OK
PL/SQL procedure successfully completed.
SQL>

Trying to update Oracle Vendor-site flexfields. Cursor not working

I'm pretty new to PL/SQL and am having some trouble getting this to run. I am trying to pass the vendor_site_id in two places using my cursor. (Eventually the list will be longer, just trying in Dev with 2 IDs)
The code was obtained from Oracle, however we are trying to pass a mass update for these flexfields.
Here it is:
SET SERVEROUTPUT ON
DECLARE
lc_return_status VARCHAR2(2000);
ln_msg_count NUMBER;
ll_msg_data LONG;
Ln_Vendor_Id NUMBER;
Ln_Vendor_site_Id NUMBER;
ln_message_int NUMBER;
Ln_Party_Id NUMBER;
lrec_vendor_site_rec ap_vendor_pub_pkg.r_vendor_site_rec_type;
CURSOR cVendorSite
IS
SELECT VENDOR_SITE_ID
FROM ap_supplier_SITES_ALL
where vendor_site_id IN ('1631833', '1630833');
BEGIN
FOR rVendorSite IN cVendorSite
LOOP
Ln_Vendor_site_Id := rVendorSite.vendor_site_id;
Lrec_Vendor_site_Rec.vendor_site_id := rVendorSite.vendor_site_id;
Lrec_Vendor_site_Rec.ATTRIBUTE1 := 'Yes';
AP_VENDOR_PUB_PKG.Update_Vendor_Site_public ( p_api_version => 1,--
x_return_status => lc_return_status, --
x_msg_count => ln_msg_count, --
x_msg_data => ll_msg_data, --
p_vendor_site_rec => Lrec_Vendor_site_Rec, --
p_Vendor_site_Id => Ln_Vendor_site_Id);
IF (lc_return_status <> 'S') THEN
IF ln_msg_count >= 1 THEN
FOR v_index IN 1..ln_msg_count
LOOP
fnd_msg_pub.get (p_msg_index => v_index, p_encoded => 'F', p_data => ll_msg_data, p_msg_index_out => ln_message_int );
Ll_Msg_Data := 'UPDATE_VENDOR_SITE '||SUBSTR(Ll_Msg_Data,1,3900);
dbms_output.put_line('Ll_Msg_Data - '||Ll_Msg_Data );
END LOOP;
End If;
END IF;
END LOOP --Cursor Loop
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('SQLERRM - '||SQLERRM );
END;
/
COMMIT;
EXIT;
END LOOP Needs a Semicolon
You have
END LOOP --Cursor Loop
It needs a semicolon:
END LOOP; --Cursor Loop
Once I modify this plsql block, I just identify two supplier_site_id's which I can modify in lieu of (1631833, 1630833). Of course, execute this for your supplier_site_id.
With this condition, you have the datatype wrong for supplier_site_id. It is of type, NUMBER, and not VARCHAR2. I would expect the database to dynamically cast these VARCHAR2's back to NUMBER's during execution.
Before I invoke the block, here are values associated with two sites I have picked (both NULL) using SQLCl:
APPS#dev>SELECT
2 attribute1
3 FROM
4 apps.ap_supplier_sites_all
5 WHERE
6 vendor_site_id IN (
7 812119,812118
8 );
ATTRIBUTE1
Next, I invoke the plsql block which calls Oracle Applications api associated with performing updates to the vendor site records:
APPS#dev>DECLARE
2 lc_return_status VARCHAR2(2000);
3 ln_msg_count NUMBER;
4 ll_msg_data LONG;
5 ln_vendor_id NUMBER;
6 ln_vendor_site_id NUMBER;
7 ln_message_int NUMBER;
8 ln_party_id NUMBER;
9 lrec_vendor_site_rec ap_vendor_pub_pkg.r_vendor_site_rec_type;
10 CURSOR cvendorsite IS
11 SELECT
12 vendor_site_id
13 FROM
14 ap_supplier_sites_all
15 WHERE
16 vendor_site_id IN (
17 812119,812118
18 );
19
20 BEGIN
21 FOR rvendorsite IN cvendorsite LOOP
22 ln_vendor_site_id := rvendorsite.vendor_site_id;
23 lrec_vendor_site_rec.vendor_site_id := rvendorsite.vendor_site_id;
24 lrec_vendor_site_rec.attribute1 := 'Yes';
25 ap_vendor_pub_pkg.update_vendor_site_public(
26 p_api_version => 1,--
27 x_return_status => lc_return_status, --
28 x_msg_count => ln_msg_count, --
29 x_msg_data => ll_msg_data, --
30 p_vendor_site_rec => lrec_vendor_site_rec, --
31 p_vendor_site_id => ln_vendor_site_id
32 );
33
34 IF
35 ( lc_return_status <> 'S' )
36 THEN
37 IF
38 ln_msg_count >= 1
39 THEN
40 FOR v_index IN 1..ln_msg_count LOOP
41 fnd_msg_pub.get(
42 p_msg_index => v_index,
43 p_encoded => 'F',
44 p_data => ll_msg_data,
45 p_msg_index_out => ln_message_int
46 );
47
48 ll_msg_data := 'UPDATE_VENDOR_SITE '
49 || substr(
50 ll_msg_data,
51 1,
52 3900
53 );
54 dbms_output.put_line('Ll_Msg_Data - ' || ll_msg_data);
55 END LOOP;
56
57 END IF;
58 END IF;
59
60 END LOOP; --Cursor Loop
61 EXCEPTION
62 WHEN OTHERS THEN
63 dbms_output.put_line('SQLERRM - ' || sqlerrm);
64 END;
65 /
PL/SQL procedure successfully completed.
Next, I check these records to see the updates we just made through calling the vendor site update api:
APPS#dev>SELECT
2 attribute1
3 FROM
4 apps.ap_supplier_sites_all
5 WHERE
6 vendor_site_id IN (
7 812119,812118
8 );
ATTRIBUTE1
Yes
Yes
Since I did not perform a commit, I rollback my changes to leave the records unchanged:
APPS#dev>rollback;
Rollback complete.
APPS#dev>SELECT
2 attribute1
3 FROM
4 apps.ap_supplier_sites_all
5 WHERE
6 vendor_site_id IN (
7 812119,812118
8 );
ATTRIBUTE1
Thus, my environment is unchanged.

Virtual Private Database policy predicate has error

I'm trying to implement a vpd. So far I have created a function:
> CREATE OR REPLACE FUNCTION sales_select(
schema_var IN VARCHAR2,
table_var IN VARCHAR2
)
RETURN VARCHAR2
IS
return_val VARCHAR2(400);
BEGIN
return_val := 'time_id >= "01-JAN-01"';
RETURN return_val;
END sales_select;
/
and the policy I made is the following:
L> BEGIN
2 DBMS_RLS.ADD_POLICY (
3 object_schema => 'sh',
4 object_name => 'costs1',
5 policy_name => 'costs_policy',
6 function_schema => 'policy_admin',
7 policy_function => 'sales_select',
8 statement_types => 'select'
9 );
0 END;
1 /
when I run the follow query:
select * from sh.costs1;
I get the following error:
ERROR at line 1:
ORA-28113: policy predicate has error
I'm thinking it has something to do with the quotes in the function but when I try changing them I get compile errors.
Is time_id a date column? If so, try changing line 9 to:
return_val := 'time_id >= date ''2001-01-01''';
Note the double quotes.

split CLOB field into table like structure

I have field in a Oracle database of type CLOB. I would like to split this field in several columns and rows. Here's an example of the content:
4:true5:false24:<p>option sample 1.</p>4:true22:<p>option sample 2.</p>5:false23:<p>option sample 3.</p>5:false22:<p>option sample 4.</p>5:false
The result should look like this:
ID LEVEL ANSWER_OPTION VALUE
1 3 option sample 3 false
1 4 option sample 4 false
2 3 option sample 3 false
4 3 option sample 3 true
3 2 option sample 2 false
1 2 option sample 2 true
2 1 option sample 1 true
2 4 option sample 4 false
4 1 option sample 1 false
2 2 option sample 2 false
4 2 option sample 2 false
1 1 option sample 1 false
3 4 option sample 4 false
4 4 option sample 4 false
3 3 option sample 3 false
3 1 option sample 1 true
We have made the following statement which created the result above.
with guest_string as
( select qsn.id id
, dbms_lob.substr( qsn.guest, 2000, 1 ) answer_options
from mneme_question qsn
where qsn.id < 10
)
select distinct id
, level
, substr(regexp_substr( answer_options'<p>[^<]+', 1, level, 'i'), 4) ANSWER_OPTION
, substr(regexp_substr( answer_options, '(true|false)', regexp_instr( answer_options, '</p>', 1, 1), level, 'i'), 1) VALUE
from guest_string
connect by regexp_substr( answer_options, '<p>[^<]+', 1, level, 'i') is not null
The problem with this code is it takes way to long to split all records we have. We had to cut it off at 10 rows (5 row take 0.25 sec, 10 takes 16 seconds, 15 rows takes about over 2,5 minutes). We currently have 30000 rows and they will grow. At the moment we cannot change the software to change the datamodel, so we will have to do this ad hoc.
Our current approach is to create a procedure that will be called for each record, but it would be better to have a faster parsing. Does anybody have a suggestion how to create a script that can do this in a reasonable time. We can do this at night time, but preferably it shouldn't take longer than 2 minutes.
BTW in the future we could build some sort of mechanism to determine which records have already been parsed, but that also requires some form of detecting already parsed fields that have changed in the mean time. We don't have time to do that yet, so for now we need crude parsing as fast as possible.
Thanks
Here is a different approach.
The code may not be as nice as it can, and you will probably have to fix some small things...
I checked it on 11g (couldn't find 10g) and used your input as the values in my clob column.
For 10 rows (all with the same input as you gave as example),
original query: 9.8 sec
new query: 0.08 sec
I used a pipelined function, here is the code:
create or replace type t_parse is object(idd number, levell number, answer_option varchar2(128), valuee varchar2(4000));
/
create or replace type tab_parse is table of t_parse;
/
create or replace function split_answers return tab_parse
pipelined is
cursor c is
select * from mneme_question;
str_t clob;
phraseP varchar2(128);
phraseV varchar2(8);
i1s number;
i1e number;
i2s number;
levell number;
begin
for r in c loop
str_t := r.guest;
levell := 1;
while str_t is not null loop
i1s := dbms_lob.instr(str_t, '<p>', 1, 1) + 3;
if i1s = 3 then
str_t := '';
else
i1e := dbms_lob.instr(str_t, '</p>', 1, 1);
phraseP := dbms_lob.substr(str_t, i1e - i1s, i1s);
str_t := dbms_lob.substr(str_t, offset => i1e + 4);
i2s := dbms_lob.instr(str_t, 'true', 1, 1) ;
if i2s = 0 then
i2s := dbms_lob.instr(str_t, 'false', 1, 1) ;
if i2s = 0 then
str_t := '';
else
phraseV := dbms_lob.substr(str_t, 5, i2s);
pipe row(t_parse(r.id, levell, phraseP, phraseV));
levell := levell + 1;
end if;
else
phraseV := dbms_lob.substr(str_t, 4, i2s);
pipe row(t_parse(r.id, levell, phraseP, phraseV));
levell := levell + 1;
end if;
end if;
end loop;
end loop;
return;
end split_answers;
/
new query should be like:
select * from table(split_answers);
There is some function whis returning pipelined table:
CREATE OR REPLACE FUNCTION split_clob(p_clob IN CLOB DEFAULT NULL,
p_varchar IN VARCHAR2 DEFAULT NULL,
p_separator IN VARCHAR2)
RETURN varchar_set
PIPELINED IS
l_clob CLOB;
BEGIN
l_clob := nvl(p_clob,
to_clob(p_varchar));
FOR rec IN (
WITH vals AS
(SELECT CAST(TRIM(regexp_substr(l_clob,
'[^'||p_separator||']+',
1,
levels.column_value)) AS
VARCHAR2(320)) AS val
FROM TABLE(CAST(MULTISET
(SELECT LEVEL
FROM dual
CONNECT BY LEVEL <=
length(regexp_replace(l_clob,
'[^'||p_separator||']+')) + 1) AS
sys.odcinumberlist)) levels)
SELECT val FROM vals)
LOOP
PIPE ROW(rec.val);
END LOOP;
RETURN;
END;
U can use it like this:
-with table with clob
SELECT t2.column_value FROM my_table t, TABLE(split_clob(p_clob => t.clob_column,p_separator => ',')) t2
-or with varchar too
SELECT * FROM TABLE(split_clob(p_varchar => '1,2,3,4,5,6, 7, 8, 9', p_separator => ','))