Concatenate CLOB-rows with PL/SQL - sql

I've got a table which has an id and a clob content like:
Create Table v_example_l (
nip number,
xmlcontent clob
);
We insert our data:
Insert into V_EXAMPLE_L (NIP,XMLCONTENT)
Values (17852,'<section><block><name>delta</name><content>548484646846484</content></block></section>');
Insert into V_EXAMPLE_L (NIP,XMLCONTENT)
Values (17852,'<section><block><name>omega</name><content>545648468484</content></block></section>');
Insert into V_EXAMPLE_L (NIP,XMLCONTENT)
Values (17852,'<section><block><name>gamma</name><content>54564846qsdqsdqsdqsd8484</content></block></section>');
I'm trying to do a function that concatenates the rows of the clob that gone be the result of a select, i mean without having to give multiple parameter about the name of table or such, i should only give here the column that contain the clobs, and it should handle the rest.
CREATE OR REPLACE function assemble_clob(q varchar2)
return clob
is
v_clob clob;
tmp_lob clob;
hold VARCHAR2(4000);
--cursor c2 is select xmlcontent from V_EXAMPLE_L where id=17852
cur sys_refcursor;
begin
OPEN cur FOR q;
LOOP
FETCH cur INTO tmp_lob;
EXIT WHEN cur%NOTFOUND;
--v_clob := v_clob || XMLTYPE.getClobVal(tmp_lob.xmlcontent);
v_clob := v_clob || tmp_lob;
END LOOP;
return (v_clob);
--return (dbms_xmlquery.getXml( dbms_xmlquery.set_context("Select 1 from dual")) )
end assemble_clob;
The function is broken... (if anybody could give me a help, thanks a lot, and i'm noob in sql so ....). Thanks!

You don't really say why it's broken but the DBMS_LOB package has an APPEND function that might be what you're looking for.

You'll have to explain what is happening that is not what you expect. Your example works fine for me. In SQLPlus, note the need to SET LONG to a large enough value to fetch the entire CLOB contents.
dev> set long 2000
dev> select assemble_clob('select xmlcontent from v_example_l') from dual;
ASSEMBLE_CLOB('SELECTXMLCONTENTFROMV_EXAMPLE_L')
--------------------------------------------------------------------------------
<section><block><name>delta</name><content>548484646846484</content></block></se
ction><section><block><name>omega</name><content>545648468484</content></block><
/section><section><block><name>gamma</name><content>54564846qsdqsdqsdqsd8484</co
ntent></block></section>

try using
DBMS_LOB.append (v_clob,tmp_lob);

Related

How to make a function that takes code like "SELECT * FROM SOME_TABLE" as an input and returns a table as an output?

I want to create a function that takes some code as an input (e.g. Select * FROM SOME_TABLE) and returns the result of a query as an output.
I want to use it in procedures in order to return tables as a result.
It should look like this:
BEGIN
--some procedure code
CREATE TABLE SOME_TABLE as Select * FROM ...;
Select * FROM table(my_function('Select * FROM SOME_TABLE'));
END;
Important tips:
The resulting table can have multiple columns, from 1 to +inft
The resulting table can have multiple rows, from 1 to +inft
So the size of a table can be both very small or very large.
The input query can have several where, having, partition, and other Oracle constructions.
I want to have a table as an output, not DBMS_OUTPUT.
I can't install any modules/applications, or use other languages hints. However, I can manually create types, functions, procedures.
I tried to search in the net but could not find a solution that meets all my expectations. The best link I've found was this:
https://sqljana.wordpress.com/2017/01/22/oracle-return-select-statement-results-like-sql-server-sps-using-pipelined-functions/
DBMS_SQL.RETURN_RESULT works if your "code" is a select query
DECLARE
l_cur SYS_REFCURSOR;
l_query VARCHAR2(4000) := 'select * from SOME_TABLE';
BEGIN
OPEN l_cur for l_query;
DBMS_SQL.RETURN_RESULT(l_cur);
END;
/
you can create a function that has a string as parameter and return a cursor.
select statement you should pass as a string. in a function you can open a Cursor.
declare
v_sql varchar2(100) := 'select 1,2,3,4,5 from dual';
cur_ref SYS_REFCURSOR;
function get_data(in_sql in varchar2) return SYS_REFCURSOR
as
cur_ret SYS_REFCURSOR;
begin
OPEN cur_ret FOR in_sql;
return cur_ret;
end;
begin
:cur_ref := get_data(v_sql);
end ;
if your select statement is longer than 32K than you maybe should use a clob instead of varchar2 for your Parameter type. But you have to try that

how to get multiple records by passing parameter to where clause in oracle pl/sql

table :
create table emp
(
E_ID number,
E_NAME varchar2(30)
);
select * from emp;
101 name1
102 name2
My code:
declare
v1 varchar2(30) := '101,102';
begin
for i in (select e_id,e_name
from emp
where e_id in (v1)) loop
dbms_output.put_line(i.e_id);
end loop;
end;
/
ISSUE:
Getting ORA -01722:invalid number
Please help to understand this issue and suggest me the solution.
It is syntax error.
E_ID is of number type and you are comparing it will v1 which is varchar2 type.
Welcome to SO. A great place to ask questions: I can see what you're trying to do. Syntactically, you'd be forgiven for trying to query your table using the "IN" clause, but as others have said, this can not be done where you have committed your numeric values into a varchar2. In anycase, an array or a collection, (even if you had created one) it isn't an easy option here. But you do have a variety of solutions open to you:
1/ Place your numbers into an Array and use a condition in your loop and check that e_id forms part of the your array. But in-elegant!
2/ Create a global temporary table and add your numbers in and add the table into your query, specify a join.
3/ Create some dynamic PL/SQL using a Ref Cursor. I've included an example for you below, using your table (emp) and values. In this case, you'd be able to build up your string according to the values you want to query. See below. The varchar2 string: sqlString can be manipulated however you want. Paste into a test-harness and see. Hope it helps
declare
type refCursor is ref cursor;
tableCursor refCursor;
emp_record emp%rowtype;
sqlString varchar2(200);
begin
-- Dynamic SQL statement with placeholder:
sqlString := 'SELECT * FROM emp WHERE e_id in
(101, 102)';
-- Open cursor:
open tableCursor for sqlString;
-- Fetch rows from result set one at a time:
loop
fetch tableCursor
into emp;
exit when tableCursor%notfound;
dbms_output.put_line(emp.e_id);
end loop;
-- Close cursor:
close tableCursor;
end;

Oracle fetch XML from BLOB

I have tried lots of methods and still can't get my full XML document from DB. What I want to achieve is displaying the XML in Oracle Apex (Display only element) but I can't manage to get the full XML out from my blob.
SELECT
utl_raw.cast_to_varchar2(dbms_lob.substr(<blob_column>, 2000, 1))
FROM
<my_table>
WHERE <some_id> = 123
Also tried to fetch it with mimetype but had no luck. Thank you.
First, you shouldn't convert it to varchar on the server side since Oracle SQL has a 4K limitation on a varchar string size. You can utilize a PL\SQL block for retrieving your data but in this case you will have a limitation in 32K. There is a special way how to get round this issue: http://mayo-tech-ans.blogspot.com/2013/06/displaying-large-clobs-in-oracle-apex.html .
Hoping, I understood the question correctly.
I think, below query will help you.
First, convert the blob column to XMLTYPE, this will also help to check, if XML is valid or not.
http://www.dba-oracle.com/t_convert_blob_to_xml_type.htm
Then, use EXTRACTVALUE to fetch the data from the XML.
select EXTRACTVALUE(xml_data,'/note/to')
from
(
select XMLTYPE.createXML('<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Test body</body>
</note>') xml_data from dual
)
;
In order to get full BLOB content:
Converted BLOB into CLOB
create or replace FUNCTION blob_to_clob (blob_in IN BLOB)
RETURN CLOB
AS
v_clob CLOB;
v_varchar VARCHAR2(32767);
v_start PLS_INTEGER := 1;
v_buffer PLS_INTEGER := 32767;
BEGIN
DBMS_LOB.CREATETEMPORARY(v_clob, TRUE);
FOR i IN 1..CEIL(DBMS_LOB.GETLENGTH(blob_in) / v_buffer)
LOOP
v_varchar := UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(blob_in, v_buffer, v_start));
DBMS_LOB.WRITEAPPEND(v_clob, LENGTH(v_varchar), v_varchar);
v_start := v_start + v_buffer;
END LOOP;
RETURN v_clob;
END blob_to_clob;
In Oracle Apex created Display Only (element) called ex: P5_XML
PLSQL code
declare
v_clob clob;
begin
SELECT blob_to_clob(<blob_column>) INTO v_clob FROM <table> WHERE <some_column> = <something>;
:P5_XML := v_clob;
end;

Looping on values, creating dynamic query and adding to result set

I have the following problem. I am an experienced Java programmer but am a bit of a n00b at SQL and PL/SQL.
I need to do the following.
1 Pass in a few arrays and some other variables into a procedure
2 Loop on the values in the arrays (they all have the same number of items) and dynamically create an SQL statement
3 Run this statement and add it to the result set (which is an OUT parameter of the procedure)
I already have experience of creating an SQL query on the fly, running it and adding the result to a result set (which is a REF CURSOR) but I'm not sure how I'd loop and add the results of each call to the query to the same result set. I'm not even sure if this is possible.
Here's what I have so far (code edited for simplicity). I know it's wrong because I'm just replacing the contents of the RESULT_SET with the most recent query result (and this is being confirmed in the Java which is calling this procedure).
Any and all help would be greatly appreciated.
TYPE REF_CURSOR IS REF CURSOR;
PROCEDURE GET_DATA_FASTER(in_seq_numbers IN seq_numbers_array, in_values IN text_array, in_items IN text_array, list IN VARCHAR2, RESULT_SET OUT REF_CURSOR) AS
query_str VARCHAR2(4000);
seq_number NUMBER;
the_value VARCHAR2(10);
the_item VARCHAR2(10);
BEGIN
FOR i IN 1..in_seq_numbers.COUNT
LOOP
seq_number := in_seq_numbers(i);
the_value := trim(in_values(i));
the_item := trim(in_items(i));
query_str := 'SELECT distinct '||seq_number||' as seq, value, item
FROM my_table ai';
query_str := query_str || '
WHERE ai.value = '''||the_value||''' AND ai.item = '''||the_item||'''
AND ai.param = ''BOOK''
AND ai.prod in (' || list || ');
OPEN RESULT_SET FOR query_str;
END LOOP;
EXCEPTION WHEN OTHERS THEN
RAISE;
END GET_DATA_FASTER;
A pipelined table function seems a better fit for what you want, especially if all you're doing is retrieving data. See http://www.oracle-base.com/articles/misc/pipelined-table-functions.php
What you do is create a type for your output row. So in your case you would create an object such as
CREATE TYPE get_data_faster_row AS OBJECT(
seq NUMBER(15,2),
value VARCHAR2(10),
item VARCHAR2(10)
);
Then create a table type which is a table made up of your row type above
CREATE TYPE get_data_faster_data IS TABLE OF get_data_faster_row;
Then create your table function that returns the data in a pipelined manner. Pipelined in Oracle is a bit like a yield return in .net (not sure if you're familiar with that). You find all of the rows that you want and "pipe" them out one at a time in a loop. When your function completes the table that's returned consists of all the rows you piped out.
CREATE FUNCTION Get_Data_Faster(params) RETURN get_data_faster_data PIPELINED AS
BEGIN
-- Iterate through your parameters
--Iterate through the results of the select using
-- the current parameters. You'll probably need a
-- cursor for this
PIPE ROW(get_data_faster_row(seq, value, item));
LOOP;
LOOP;
END;
EDIT: Following Alex's comment below, you need something like this. I haven't been able to test this but it should get you started:
CREATE FUNCTION Get_Data_Faster(in_seq_numbers IN seq_numbers_array, in_values IN text_array, in_items IN text_array, list IN VARCHAR2) RETURN get_data_faster_data PIPELINED AS
TYPE r_cursor IS REF CURSOR;
query_results r_cursor;
results_out get_data_faster_row := get_data_faster_row(NULL, NULL, NULL);
query_str VARCHAR2(4000);
seq_number NUMBER;
the_value VARCHAR2(10);
the_item VARCHAR2(10);
BEGIN
FOR i IN 1..in_seq_number.COUNT
LOOP
seq_number := in_seq_numbers(i);
the_value := trim(in_values(i));
the_item := trim(in_items(i));
query_str := 'SELECT distinct '||seq_number||' as seq, value, item
FROM my_table ai';
query_str := query_str || '
WHERE ai.value = '''||the_value||''' AND ai.item = '''||the_item||'''
AND ai.param = ''BOOK''
AND ai.prod in (' || list || ');
OPEN query_results FOR query_str;
LOOP
FETCH query_results INTO
results_out.seq,
results_out.value,
results_out.item;
EXIT WHEN query_results%NOTFOUND;
PIPE ROW(results_out);
END LOOP;
CLOSE query_results;
END LOOP;
END;
Extra info from Alex's comment below useful for the answer:
you can have multiple loops from different sources, and as long as the
data from each be put into the same object type, you can just keep
pumping them out with pipe row statements anywhere in the function.
The caller sees them as a table with the rows in the order you pipe
them. Rather than call a procedure and get a result set as an output
parameter, you can query as select seq, value, item from
table(package.get_data_faster(a, b, c, d)), and of course you can
still have an order by clause if the order they're piped isn't what
you want.

How can I fetch the data from the SYS_REFCURSOR from one stored proc and use it in another?

I have a stored proc with the basic layout below that returns a sys_refcursor as a result set. (Technically it reurns four but for the sake of clarity I just say one). The result set is a selection from a temp table.
procedure aProcedure
( C1 in out sys_refcursor
) is
begin
--populate Temp_Table here with a stored proc call;
OPEN C1 FOR
SELECT Cols
FROM TEMP_TABLE;
I need to insert this result set from C1 into a permanent table using a different stored procedure. Is this Do-able or do I need to re-build the result set all over again?
I've been able to find information on working with cursors and result sets outside of oracle but not for working with them within itself.
I know it might make sense to just do the insert from the first stored proc but that's not really how I need it to happen. It's an optional requirement to save the result set permanently.
Thanks for any helpful info.
Assuming that the caller knows the structure of the cursor that aProcedure is opening, you can do something like this.
declare
l_rc sys_refcursor;
l_rec temp_table%rowtype;
begin
aProcedure( l_rc );
loop
fetch l_rc
into l_rec;
exit when l_rc%notfound;
dbms_output.put_line( l_rec.col1 );
end loop;
close l_rc;
end;
/
If you can't fetch into a record type, you can also fetch into a number of other scalar local variables (the number and type have to match the number and type of columns that aProcedure specifies in its SELECT list). In my case, I defined aProcedure to return two numeric columns
declare
l_rc sys_refcursor;
l_col1 number;
l_col2 number;
begin
aProcedure( l_rc );
loop
fetch l_rc
into l_col1, l_col2;
exit when l_rc%notfound;
dbms_output.put_line( l_col1 );
end loop;
close l_rc;
end;