I have a function in Oracle 21 version that return a CLOB variable, let's call it x (more than 4000 bytes)
and I'm selecting this variable to display it in a classic report in Oracle Apex 21
But I always got the error: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: 7258, maximum: 4000)
I tried also dbms.lob.substr but it won't work.
Is there any solution to this ?
Perhaps you should think it over - who wants to read CLOB contents on the screen? That's just too much text. A better option might be to let users download CLOB contents, if they want (and display e.g. the first 100 or 200 characters).
Otherwise, see if switching to a PL/SQL Dynamic Content region type helps. It means that you'd actually "draw" the report manually, reading CLOB contents in a loop and create the region using htp.p calls. Sample code is available on Universal theme pages. In case it becomes unavailable, here it is:
declare
cursor c_tasks is
select task_name, assigned_to
from eba_ut_chart_tasks
where rownum < 5;
begin
sys.htp.p('');
for a in c_tasks
loop
sys.htp.p('' || apex_escape.html(a.task_name) || ' (' || apex_escape.html(a.assigned_to) || ')' );
end loop;
sys.htp.p('');
end;
It's up to you, but ... I'd go for the 1st option.
Related
This question already has answers here:
Oracle - convert column values to comma separated values as CLOB without using XMLAGG
(1 answer)
ORA-64451: Conversion of special character to escaped character failed
(1 answer)
Closed 8 months ago.
select t.name, listagg(t.text)
from user_source t
group by t.name;
I am trying to execute the code above but since varchar2 is limited by 4000 chars it throws error. I tried to convert listagg to xml but I could not solve the
ORA-64451: Conversion of special character to escaped character failed.
error. I also tried the answers from other posts from various websites including stackoverflow.
I do not want to truncate the string, also I can't change MAX_STRING_SIZE parameter.
This example below throws ORA-64451 as well. I tried but could not solve the problem.
select rtrim(
xmlagg(
xmlelement(e, to_clob(t.TEXT), '; ').extract('//text()')
).GetClobVal(),
',')
from user_source t;
The best solution I know is posted somewhere in the Internet... You could probably just google for it. It basically consist of few steps:
Creating a collection type to store each text value to concatenate
create or replace type string_array_t as table of VARCHAR2(4000);
Creating a PL/SQL function which takes string_array_t as parameter and returns concatenated text as CLOB:
create or replace function
string_array2clob(
p_string_array string_array_t
,p_delimiter varchar2 default ','
) RETURN CLOB IS
v_string CLOB;
BEGIN
-- inside is a loop over p_string_array to concatenate all elements
--
-- below is just a draft because in real you should use a while loop
-- to handle sparse collection and you should put some checks to handle not initialized collection
-- and other important cases
-- furthermore it's better to create additional varchar2 variable as a buffer
-- and convert that buffer to clob when it's full for a better performance
for indx in p_string_array.first..p_string_array.last loop
v_string := v_string || to_clob(p_string_array(indx) || p_delimiter);
end loop;
RETURN substr(v_string, 1, nvl(length(v_string),0) - nvl(length(p_delimiter),0));
END string_array2clob;
/
Aggregate query as usual but using cast and collect instead of listagg and at the end convert it to clob with function from step above:
select t.name, string_array2clob(cast(collect(t.text order by t.line) as string_array_t ), p_delimiter => chr(10)) as text
from user_source t
group by t.name;
If your query is not just an example of concept and really you're trying to get a source of some object in database, then you should read about dbms_metadata.get_ddl function. It's made for it.
I have text stored in a database table, many short rows about 70-90 characters long fields. (of historical reasons). I want to append these fields (rows) into an CLOB in an APEX (CKEditor) and it does exeed 32k in many cases.
I have tried in many ways but it seems to be some limit. My code works fine as long the text is less than 32k! My plan is to save it in a new table and there use a clob instead. I have APEX 5.01.
I get 'ORA-06502: PL/SQL: numeric or value error' when it is over 32k.
declare
l_clob CLOB;
l_seq number;
cursor textrader_cur is
SELECT F1NR,FHTTYP,RADNR,FHTEXT,DATUM,UPPTAGEN,NUSER FROM DATATXT WHERE DATATXT.F1NR = :P10_F1NR ORDER BY F1NR,FHTTYP,RADNR;
TYPE datatext_typ IS TABLE OF DATATXT%ROWTYPE INDEX BY PLS_INTEGER;
l_datatext datatext_typ;
begin
l_clob := empty_clob();
DBMS_LOB.CREATETEMPORARY(l_clob,true);
apex_collection.create_or_truncate_collection(p_collection_name => 'TEXT');
select count(1) into x from DATATXT#HUMANAUTV WHERE DATATXT.F1NR = :P10_F1NR;
if x > 0 then
open textrader_cur;
loop
fetch textrader_cur bulk collect into l_datatext LIMIT 200;
for indx in 1..l_datatext.COUNT loop
y := length(l_datatext(indx).fhtext);
dbms_lob.writeappend (l_clob,y,l_datatext(indx).fhtext);
--l_clob := l_clob || l_datatext(indx).fhtext; -- This causes same error
end loop;
EXIT WHEN l_datatext.COUNT = 0;
end loop;
close textrader_cur;
l_seq := apex_collection.add_member(p_collection_name => 'TEXT',
p_d001 => sysdate,
p_d002 => sysdate,
p_n001 => dbms_lob.getlength(l_clob),
p_clob001 => l_clob);
-- :P10_WP := l_clob;
SELECT clob001 into :P10_WP FROM APEX_COLLECTIONS WHERE SEQ_ID = l_seq AND COLLECTION_NAME='TEXT';
end if;
end;
PL/SQL provides a dbms_lob package to manipulate this data type. The way I had addressed in a different technology (zope/python) similar problem was to create a framework:
To read from db, it returned data as multipe rows
To write to db, it would send data as multipel calls and server eventually combined it.
You can see that here Blob Journey from Database To Browser
The problem is the last line in your code. Session state variables (e.g. P10_WP) are all VARCHAR2, and limited to 32767 characters. You can see that in the APEX functions that call them (example). So you can't assign more than 32k characters to a PL/SQL page item.
But obviously you can put more than 32k characters in an HTML form item! So it's an awkward workaround - you have to get the clob data in and out of the HTML form item without using APEX page items. Typically this is done by writing the clob to a Collection, then using AJAX calls to an Application Process to retrieve it, since JavaScript has no problem with character limits.
It looks like you've gotten partway there on your own, with your TEXT collection, but you'll still have to write your own On-Demand Application Process so you can load the collection into JavaScript, and put it in the HTML form item from there. It'll be easier if you use the built-in apex.ajax.clob functionality with the CLOB_CONTENT collection.
I looked over a few articles about this, and this one is pretty well written and straightforward.
The short version is to change your collection name in your code to CLOB_CONTENT, then put this JavaScript function on your page and call it.
function clob_get(){
var clob_ob = new apex.ajax.clob(
function(){
var rs = p.readyState
if(rs == 1||rs == 2||rs == 3){
$x_Show('AjaxLoading');
}else if(rs == 4){
$s('P10_WP',p.responseText);
$x_Hide('AjaxLoading');
}else{return false;}
}
);
clob_ob._get();
}
PL/SQL in APEX are limited to 32k, pl/sql treats clobs as varchar and thats it. My problem cannot be solved within APEX
My requirement is to insert img src values into table and then display in on the apex page.
How can I do that?
I have created a function which inserts the img src into CLOB column
But incase the length exceeds 32000 it doesnt insert it to the CLOB column
The fact that your data is getting truncated at 32000 characters (probably actually 32767) means you have some intermediate VARCHAR2. Other than that, there's not enough information here.
Once you get your base64 encoded data, to display it on an Apex page, the easiest way to do that is with a PL/SQL region and the htp package. None of the native htp functions support CLOBs, so you will have to output it in chunks. Something like this:
i:= 1;
loop
l_chunk := dbms_lob.substr( l_b64_clob, l_chunk_size, i );
exit when l_chunk is null;
htp.prn( l_chunk );
i := i + l_chunk_size;
end loop;
I suggest you write a reusable procedure to do this.
Declaration of PL/SQL variable require setting length for varchar2 like types:
declare
query varchar2(2000);
begin
for i in 1..100 loop
query := query || to_char(i);
end loop;
dbms_output.put_line(query);
end;
/
I run in trap when product requirements changes so loop range become larger (set range to 1..1000 in my example) and I got error:
ORA-06502: PL/SQL: : буфер символьных строк слишком маленький ошибка числа или значения ORA-06512: на line 5
So I need to increase query length everywhere and even with some larger number to predicate future.
This is ugly. I am not expert of PL/SQL but by reading official docs I found clob type which doesn't require length and quick checks show me that code works!
PL/SQL code become less Pascal like and more Python/Ruby/Perl like. I really like it.
I apply type conversion only for dynamically built strings which are out of intensive loop usages.
Is such technique correct or I am wrong?
PS I am freshman to PL/SQL programming so ask such dumb question...
PPS One of working code (simplified):
queryStr := 'create or replace view "DATA_VIEW" ("DT", "MONEY", "EVENT") as ( ';
for eventRec in eventCur
loop
queryStr := queryStr || 'select DT, MONEY, ' || eventRec.ID
|| ' EVENT from ' || eventRec.tbl || ' union ';
end loop;
queryStr := rtrim(queryStr, ' union ') || ')';
execute immediate queryStr;
commit;
The CLOB datatype is your only real option if you want to manipulate strings larger than 4000 bytes in SQL or 32 kB in PL/SQL.
The operations on CLOB are a bit more expensive than on VARCHAR2 for small strings though, so you should only use the CLOB datatype when dealing with large strings. Thus the VARCHAR2 datatype still has its uses, don't go overboard and replace all your strings with CLOBs.
Looking at your example working code, what I understand is you want to dynamically generate a view definition which is made up of probably hundreds of union statements, each querying a different table.
Similar data stored in hundreds of different tables! Now thats a strange database architecture!!
anyways, just blindly replacing varcar2 with clob is not recommeneded from performance point of view.
Why dont you keep a counter/check for the number of records in your 'eventCur'(eventRec)?
Then you can optimally choose a value for that counter above which you could use clob datatype and below which a varchar2 datatype..
I got a problem with oracle database ,i created a stored procedure and i wanted to pass an array of items' ids to this procedure to select the data according to array of items using "in" clause,the available solution to this as i found was to create a function and pass a string value with all item's ids seperated by a comma ,and this function will return a datatble with a row for each item id.this approach works fine when i try it in toad in a select statement,,but when i use it in the stored procedure i get a strange error
"ORA-12714: invalid national character set specified"
after searching about the reason of that error i found that it is a bug in that version of oracle according to this page and it was fixed in a 10.2.0.4 oracle patch and the exact reason is to declare a cursor for the function that returns a data table
As it is impossible to me to let the users who work on a live production environment to stop the servers to apply the update patch ,I was wondering if any Oracle expert can help me to declare a cursor and return that cursor instead of returning the table.
my Oracle function,Thanks in Advance
create or replace
FUNCTION SplitIDs(
v_List IN VARCHAR2)
RETURN RtnValue_Set PIPELINED
AS
SWV_List VARCHAR2(2000);
v_RtnValue Dt_RtnValue := Dt_RtnValue(NULL);
BEGIN
SWV_List := v_List;
WHILE (instr(SWV_List,',') > 0)
LOOP
FOR RetRow IN
(SELECT ltrim(rtrim(SUBSTR(SWV_List,1,instr(SWV_List,',') -1))) SelectedValue
FROM dual
)
LOOP
v_RtnValue.SelectedValue := RetRow.SelectedValue;
PIPE ROW(v_RtnValue);
END LOOP;
SWV_List := SUBSTR(SWV_List,instr(SWV_List,',')+LENGTH(','),LENGTH(SWV_List));
END LOOP;
FOR RetRow IN
(SELECT ltrim(rtrim(SWV_List)) SelectedValue FROM dual
)
LOOP
v_RtnValue.SelectedValue := RetRow.SelectedValue;
PIPE ROW(v_RtnValue);
END LOOP;
RETURN;
END;
Oracle says this about the error:
Error: ORA-12714 (ORA-12714)
Text: invalid national character set specified
Cause: Only UTF8 and AL16UTF16 are allowed to be used as the national character set
Action: Ensure that the specified national character set is valid
Check your NLS_NCHAR_CHARACTERSET which is set using:
select value from NLS_DATABASE_PARAMETERS where parameter = 'NLS_NCHAR_CHARACTERSET';
Try using NCHAR, NVARCHAR2 or NCLOB