IS it possible to concatenate A1 and A2 from the particular table (for example):
CREATE TABLE MY_SCHEME.CONC_BLOB
(
A1 BLOB,
A1_SORT NUMBER(20),
T_TYPE VARCHAR2(9 BYTE),
A2 BLOB,
A2_SORT NUMBER(20),
A3 VARCHAR2(32 BYTE),
A4 BLOB,
A5 VARCHAR2(8 BYTE)
)
?
How?
BLOBs can be concatenated with the DBMS_LOB package, in particular with the APPEND procedure. But you will need to use some PL/SQL that iterates over the relevant rows and calls the procedure.
I don't quite understand what you mean by next table so I can't give you an example.
Update:
The relevant piece of PL/SQL could look like this:
DECLARE
a1_lob BLOB;
a2_lob BLOB;
BEGIN
SELECT A1, A2 INTO a1_lob, a2_lob
FROM CONC_BLOB
WHERE A1_SORT = 'some value'
FOR UPDATE;
dbms_lob.append(a1_lob, a2_lob);
COMMIT;
END;
FYI: if you intent to use blob to store large text (that's why I suppose you would like to concatenate them) I suggest using CLOB. It will permit you to use || for the best part of concatenations. Unfortunately you could face with the issue of || when the length of clob exceeds 32767
Here is my solution for joining any number of BLOBs into single BLOB using helper table type and stored function:
create or replace type blobs as table of blob;
create or replace function concat_blobs(parts in blobs) return blob
is
temp blob;
begin
if parts is null or parts.count = 0 then
return null;
end if;
dbms_lob.createtemporary(temp, false, dbms_lob.CALL);
for i in parts.first .. parts.last
loop
dbms_lob.append(temp, parts(i));
end loop;
return temp;
end;
-- usage example:
select concat_blobs(blobs(to_blob(hextoraw('CAFE')), to_blob(hextoraw('BABE')))) from dual;
-- bonus
create or replace type raws as table of raw(2000);
create or replace function raws_to_blobs(arg in raws) return blobs
is
res blobs;
begin
select to_blob(column_value) bulk collect into res from table(arg);
return res;
end;
-- usage example:
select concat_blobs(raws_to_blobs(raws(hextoraw('CAFE'), hextoraw('BABE'))) from dual;
See also multiple RAWs concatenation in Oracle 10: Using HEXTORAW to fill in blob data.
Related
I have a cursor which fetches records from a table.
open p_cursor for select a1, a2 from my_table;
Thereafter I use fetch to get the columns and put all of them into a single CLOB column as follows : ( add_to_clob is a procedure which concatenates a text into existing CLOB - my_clob )
fetch p_cursor into l_a1, l_a2;
add_to_clob ( my_clob, l_a1 );
add_to_clob ( my_clob, l_a2 );
Essentially - the output of fetch are being written into a large CLOB.
But the operation is running slower than expected; and we have millions of records to process.
Is there any way such that use of cursor can be avoided to that the process runs faster ?
You can "cheat" a bit and use some XML aggregate functions like this:
DECLARE
l_clob1 CLOB;
l_clob2 CLOB;
BEGIN
SELECT XMLSERIALIZE(CONTENT EXTRACT(XMLAGG(XMLELEMENT(COL1, ao.object_name||', ')), '/COL1/text()') AS CLOB),
XMLSERIALIZE(CONTENT EXTRACT(XMLAGG(XMLELEMENT(COL2, ao.edition_name||', ')), '/COL2/text()') AS CLOB)
INTO l_clob1, l_clob2
FROM all_objects ao
WHERE ROWNUM <= 10;
END;
/
Just replace the simple query with your own. Also, you'll need to
I can not fetch value from my hash table of object type to refcursor.
Please advice me for your solution.
I have types:
CREATE OR REPLACE TYPE RULE_SET_TYPE AS OBJECT
(
AR_ID NUMBER (18),
TMN_CODE VARCHAR2 (8),
RULE_ID NUMBER (10),
RULE_PRIORITY NUMBER (3),
ATTRB_KEY VARCHAR2 (100),
ATTRB_VAL VARCHAR2 (100)
);
CREATE OR REPLACE TYPE RULE_SET_TABLE IS TABLE OF RULE_SET_TYPE;
And in my packages, I using as:
--In Pack Spec
CREATE OR REPLACE PACKAGE MSV_API
AS
TYPE REFCURSOR IS REF CURSOR;
TYPE RULE_HASH_TABLE IS TABLE OF RULE_SET_TYPE
INDEX BY VARCHAR2 (100);//I can't not create this type at schema level, that cause by declare in package
.................
-- In Pack Body
PROCEDURE GET_APPY_RULES (P_TMN_CODE IN VARCHAR2(8),
P_CUR OUT REFCURSOR)
AS
RULE_SET_VAL RULE_SET_TABLE;//Temp object for data processing
RULE_SET_RESULT RULE_SET_TABLE; //
RULE_HASH RULE_HASH_TABLE;//using of hash array type
K VARCHAR2 (100); //
BEGIN
............
-- OPEN p_cur FOR SELECT * FROM table(RULE_HASH); not working:ORA-22905: cannot access rows from a non-nested table item
RULE_SET_RESULT := RULE_SET_TABLE ();
K := RULE_HASH.FIRST;
WHILE K IS NOT NULL
LOOP
--DBMS_OUTPUT.PUT_LINE (K || ' : ' || RULE_HASH (K).ATTRB_VAL);
RULE_SET_RESULT.EXTEND;
RULE_SET_RESULT (RULE_SET_RESULT.COUNT) := RULE_HASH (K);
K := RULE_HASH.NEXT (K);
END LOOP;
OPEN P_CUR FOR SELECT * FROM TABLE (RULE_SET_RESULT);
END;
My problem is can not fetch data to refcursor from hash array in Oracle. And I convert from hash array to Table of Object by looping. I want to get better solution for this way. ** -- OPEN p_cur FOR SELECT * FROM table(RULE_HASH); not working:ORA-22905: cannot access rows from a non-nested table item
I know the fetch data into other collection by looping hash array is not good for performance.
Have any idea for my problems?
Thank you for reading.
Mostly I avoid table variables as input parameters for a stored procedure. Because I do not know how to handle them, but in this case I have no other option. I have a requirement where hundreds of records will be passed on to database from Oracle Agile PLM. What I have to do is to populate a table from the input records/list. For accomplishing this I have developed an object type and then a table type out of that object type.
CREATE OR REPLACE TYPE TEST_USER.MD_TYPE AS OBJECT
(QUERY_REF VARCHAR2 (1000 BYTE),
COL_NAME VARCHAR2 (100 BYTE),
COL_LENGTH VARCHAR2 (50 BYTE),
COL_SEQ NUMBER)
/
CREATE OR REPLACE TYPE TEST_USER.MD_TYPE_TABLE AS TABLE OF MD_TYPE
/
Stored Procedure:
CREATE OR REPLACE PROCEDURE SP_TEST2
(
P_MD_TABLE IN MD_TYPE_TABLE,
p_success OUT number
)
IS
BEGIN
INSERT INTO MDATA_TABLE
(
QUERY_REF ,
COL_NAME ,
COL_LENGTH ,
COL_SEQ
)
SELECT ea.*
FROM TABLE(P_MD_TABLE) ea;
p_success :=1;
EXCEPTION
WHEN OTHERS THEN
p_success := -1;
END SP_TEST2;
The problem is I do not know how to populate, first parameter P_MD_TABLE and then MDATA_TABLE. And the procedure compiles without any errors. I have not tested this procedure.
Any help please.
Procedure for loading MD_TYPE_TABLE by passing parameters to MD_TYPE
CREATE OR REPLACE PROCEDURE SP_UPLOAD_MD_TYPE
(
P_QUERY_REF VARCHAR2,
P_COL_NAME VARCHAR2,
P_COL_LENGTH VARCHAR2,
p_col_seq NUMBER,
p_no_of_rows_to_insert NUMBER,
p_num OUT NUMBER
)
IS
p_type_tbl MD_TYPE_TABLE := MD_TYPE_TABLE(); --initialize
BEGIN
<<vartype>>
FOR i IN 1..p_no_of_rows_to_insert
LOOP
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE(P_QUERY_REF, P_COL_NAME, P_COL_LENGTH, p_col_seq);
END LOOP vartype;
SP_TEST2(p_type_tbl, p_num);
END;
You can populate a table type by using extend/ bulk collect
using extend
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF1', 'COL_NAME1', 'COL_LENGTH1', 1);
or using bulk collect
SELECT MD_TYPE(c1, c2... cn)
BULK COLLECT INTO p_type_tbl
FROM some_table;
Demo
DECLARE
p_type_tbl MD_TYPE_TABLE := MD_TYPE_TABLE(); --initialize
p_num NUMBER;
BEGIN
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF1', 'COL_NAME1', 'COL_LENGTH1', 1);
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF2', 'COL_NAME2', 'COL_LENGTH2', 2);
SP_TEST2(p_type_tbl, p_num);
DBMS_OUTPUT.PUT_LINE(p_num);
END;
/
OutPut
1
SELECT * FROM MDATA_TABLE;
OutPut
QUERY_REF COL_NAME COL_LENGTH COL_SEQ
QUERY_REF1 COL_NAME1 COL_LENGTH1 1
QUERY_REF2 COL_NAME2 COL_LENGTH2 2
The idea is, I want to clone the record as a CLOB when it is updated.
Why do it in such a way?
There are two different applications A1 and A2, A1 is depended on by A2.
Based on A1 values, calculations are made for values for A2.
The A2 process runs just once per day to calculate the values, but for A1 every field in the TABLE_NAME in question can be altered several times a day and doesn't have a history.
The aim is to create a history which is a CLOB field in a table "NEW_TABLE" of automatic form.
Sorry for my English, but if something is not understandable I can rewrite the question
My Code Here:
CREATE or REPLACE TRIGGER TRIGGER_NAME
AFTER UPDATE
ON TABLE_NAME
FOR EACH ROW
DECLARE
row_record NEW_TABLE%rowtype;
c_xml CLOB;
FUNCTION GetXML(a_tablela varchar2, a_key_1 varchar2, a_key_2 varchar2)
RETURN CLOB
is
x_xml CLOB;
BEGIN
select dbms_xmlgen.getxml('select * from '||a_tablela||' where key_1 = '''||a_key_1||''' and key_2 = '''||a_key_2||'''') into x_xml from dual;
return x_xml;
END;
BEGIN
--** TABLE_NAME Automatically fetches all columns and transforms them to CLOB
c_xml := GetXML('TABLE_NAME', :new.key_1, :new.key_2);
if c_xml is not null then
row_record.TABLE_NAME :=c_xml;
end if;
INSERT INTO NEW_TABLE VALUES row_record;
EXCEPTION
when others then
raise_application_error(-20000,'ERROR: '||to_char(sqlcode));
END;
Now I get error:
ORA-04091: table TABLE_NAME is mutating, trigger/function may not see it.
when I get this record across SELECT statement.
How do I convert row into CLOB in the applied TRIGGER AFTER UPDATE ?
Thanks.
The reason you can't use a select statement is because you're in the trigger, and the table is changing, or 'mutating', as the error says. The only way you can get the data from the row that's being updated here is using new and old:
old.column1
new.column1
Old being the value of the column before the update, new being the value after the update.
Example:
CREATE or REPLACE TRIGGER TRIGGER_NAME
AFTER UPDATE
ON TABLE_NAME
FOR EACH ROW
BEGIN
l_string := 'This is the old value for column 1: ' || old.column1 || '. This is the new value: ' || new.column1;
dbms_output.put_line(l_string);
END;
You won't be able to use dbms_xmlgen because it uses a select statement, which throws the mutating error exception.
I'm not sure I perfectly understand what you're trying to do, but you should be able to build the CLOB yourself just by concatenating yourself with the column names. Like this:
CREATE or REPLACE TRIGGER TRIGGER_NAME
AFTER UPDATE
ON TABLE_NAME
FOR EACH ROW
BEGIN
l_clob := 'Column1 ' || old.column1 || ', Column2 ' || old.column2; --For as many columns as are in the table
--Now you have a clob with all the old values, insert it where you want it
END;
And then go from there. If you really want the XML format you can do that yourself as well, just concatenate the strings together.
I have to write an Oracle procedure which should invoke an Oracle function returning REF_CURSOR. The function is declared like that
FUNCTION "IMPACTNET"."TF_CONVERTPARA" (PARASTRING IN NVARCHAR2) RETURN SYS_REFCURSOR
AS
c SYS_REFCURSOR;
BEGIN
OPEN c FOR
SELECT SUBSTR(element, 1, INSTR(element, '|') - 1) as key,
SUBSTR(element, INSTR(element, '|') + 1, 99999) as val
FROM (
SELECT REGEXP_SUBSTR(PARASTRING, '[^;]+', 1, LEVEL) element
FROM dual
CONNECT BY LEVEL < LENGTH(REGEXP_REPLACE(PARASTRING, '[^;]+')) + 1
);
RETURN c;
END;
Can you tell me what I need to write in order to invoke the function from within my procedure? I'd like to insert all the returned values (shaped a table with two columns) into a rational table.
Thank you in advance!
Something along the lines of this should work (obviously, I'm guessing about table names and column names and the exact logic that you're trying to implement)
CREATE PROCEDURE some_procedure_name
AS
l_rc SYS_REFCURSOR := impactnet.tf_convertpara( <<some string>> );
l_key VARCHAR2(100);
l_val VARCHAR2(100);
BEGIN
LOOP
FETCH l_rc
INTO l_key, l_val;
EXIT WHEN l_rc%notfound;
INSERT INTO some_table( key_column, val_column )
VALUES( l_key, l_val );
END LOOP;
END;
As Ollie points out, it would be more efficient to do a BULK COLLECT and a FORALL. If you're just dealing with a few thousand rows (since your function is just parsing the data in a delimited string, I'm assuming you expect relatively few rows to be returned), the performance difference is probably minimal. But if you're processing more data, the difference can be quite noticeable. Depending on the Oracle version and your specific requirements, you may be able to simplify the INSERT statement in the FORALL to insert a record rather than listing each column from the record individually.
CREATE PROCEDURE some_procedure_name
AS
TYPE key_val_rec
IS RECORD(
key VARCHAR2(100),
val VARCHAR2(100)
);
TYPE key_val_coll
IS TABLE OF key_val_rec;
l_rc SYS_REFCURSOR := impactnet.tf_convertpara( <<some string>> );
l_coll key_val_coll;
BEGIN
LOOP
FETCH l_rc
BULK COLLECT INTO l_coll
LIMIT 100;
EXIT WHEN l_coll.count = 0;
FORALL i IN l_coll.FIRST .. l_coll.LAST
INSERT INTO some_table( key_column, val_column )
VALUES( l_coll(i).key, l_coll(i).val );
END LOOP;
END;