Does oracle have a method to get substring based on number of bytes from a CLOB field?
select DBMS_LOB.SUBSTR(a.COMMENTS, 3998, 1)
FROM FOO;
I am getting error:
"ORA-06502: PL/SQL: numeric or value error: character string buffer
too small"
.
The problem was in special characters. Each new special character takes 8 bytes so when I reduce the string limit to 3992 then it works.
DBMS_LOB.SUBSTR(a.COMMENTS, 3992, 1) works.
For testing purpose I put many special characters and again it throws same error.
Does oracle have any method which finds substring based on number of bytes than number of characters?
Actually, we are fetching data from a table and need to display on UI with a limitation of 4000 characters. So, we want to fetch first 4000 characters only. As, a character size is 1 byte, we can accomodate 4000 bytes. So, if we use DBMS_LOB.CONVERTTOBLOB, we may not be able to display properly the characters string fetched. Can we convert it back it charater string somehow?
I used the old SUBSTR function successfully, which works for the clob type as well. In this case, it's SUBSTR(a.COMMENTS, 1, 3992)
It's an old problem, but I did not find any solution to it, so I will post my approach to find solution for this problem, just in case someone needs to handle this...
My task was to retrieve "the most" characters from CLOB..
DBMS_LOB.SUBSTR nor SUBSTR will achieve the correct result...
In case of using DBMS_LOB.SUBSTR I keep getting ORA-12801 with ORA-06502
Using SUBSTR another ORA-64203..
Suggestions say to do SUBSTR for max 1000, because maximum number of bytes for character is 4, therefore You won't get more then 4000 bytes, unfortunately that's not good enough for me, because You might receive only 2000 bytes this way, in case that certain row does not contain multi-byte characters...
My solution, which shouldn't be used for repeated queries by the way (rather for extracting and loading/storing data into VARCHAR2 column), is:
create or replace FUNCTION SUBSTR_MULTIBYTE_CLOB
(
P_DATA IN CLOB
, P_START_INDEX IN NUMBER
) RETURN VARCHAR2 AS
P_OUT VARCHAR2(4000 BYTE);
P_LENGTH NUMBER := 4000;
BEGIN
FOR loop_counter IN 1..400 LOOP
BEGIN
P_OUT := DBMS_LOB.SUBSTR(P_DATA,P_LENGTH-((loop_counter-1)*10),P_START_INDEX);
RETURN P_OUT;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -12801 OR SQLCODE = -6502 OR SQLCODE = -1401 OR SQLCODE = -1489 THEN
NULL; -- suppresses ORA-12801 "error signal from parallel server" or ORA-06502 exception "character string buffer too small" and some others I've got...
ELSE
RAISE;
END IF;
END;
END LOOP;
END SUBSTR_MULTIBYTE_CLOB;
If needed, You can change loop to 4000 and decrease it by one byte, I've decided to go by 10, just to make it little bit faster...
try this method::
Use Oracle function LENGTHB() to get this result. there is a way around convert CLOB to BLOB by using DBMS_LOB.CONVERTTOBLOB and use DBMS_LOB.GET_LENGTH(). this is will return no of bytes.
You can use this thread for complete answer ::
https://forums.oracle.com/forums/thread.jspa?threadID=2133623
First, try to replace all special characters from the Character string and then try to substring it to convert it to Character.
Example:- DBMS_LOB.SUBSTR(REGEXP_REPLACE(your_column, '[^0-9A-Za-z]', ''),3999,1)
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 tried both[CAST & TO_NUMBER] method but non of worked to cast values that is returned from another procedure.
SELECT DBMS_LOB.SUBSTR(SUBSTR(DETAIL_LOB, 2300, 4), 2000)
INTO RES
FROM lONG_TO_CLOB_TEMP_TABLE;`
RES VALUE IS 40 returned by above query.
RES datatype is varchar2.
Now I want to convert that RES value into number so that I can do some arithmetic operation with this.
like => 100/RES
Not working code =>
V_TEMP := CAST(RES AS NUMBER)
V_TEMP := TO_NUMBER(RES)
Error Message => "ORA-06502: PL/SQL: numeric or value error"
Its most likely special character involve in (space or line break)
Try
V_TEM := To_number(regexp_replace(trim(RES), '(^[[:space:]]+)|([[:space:]]+$)',null))
There was a line break causing the problem.
Solved with:-
REPLACE(REPLACE(REPLACE(column_name, CHR(10)), CHR(13)), CHR(9));
--Special thanks to #KaushikNayak
In order to remove a newline character using PL/SQL, use the following:
declare charwithnewline VARCHAR2(10) := CONCAT('190', CHR(13));
begin
dbms_output.put_line('+++++++++++++++++++++++++');
dbms_output.put_line(charwithnewline);
dbms_output.put_line('+++++++++++++++++++++++++');
dbms_output.put_line(REPLACE(charwithnewline,CHR(13),''));
dbms_output.put_line('+++++++++++++++++++++++++');
end;
Note: The newline character could be CHR(13) or CHR(10) or a combination of these two characters. The logic is the same.
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..
Here's the code... this isn't the complete code. I trimmed it down to where the first error occurred:
FUNCTION get (
p_sql_o OUT VARCHAR2
) RETURN VARCHAR2 AS
str_sql VARCHAR2(4000);
BEGIN
str_sql := ' SELECT * FROM ( SELECT A.*, ROWNUM RNUM FROM ( ' ||
' SELECT item_code, ' ||
' item_desc, ' ||
' monitor, ' ||
' measured, ' ||
' inventory, ' ||
' (measured - inventory) adj_amount, ' ||
' (inventory_cost * measured) measured_cost, ' ||
'inventory';
RETURN str_sql;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END get;
Obviously, the SQL is incomplete, but I'm not running it. I'm simply returning the SQL string, yet I still get an error:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 6
This is baffling. Does anyone have any clue as to why this would be the case?
Since you've said you aren't attempting to dynamically execute the SQL you're creating, the syntax and correctness of what's in there, or the length of what it might return, is clearly irrelevant. You aren't doing anything with your p_sql_o OUT parameter, so that isn't causing this problem either. That only really leaves str_sql as the culprit, and as Justin implied yesterday, it's declared as large enough within the function and the function itself compiles OK - so it looks like it has to be how it's being called that's the problem.
You mentioned it works if you remove the 'inventory', which reduced the length of that string from 201 to 192 characters, so I'm guessing you've got this set to 200 chars in the caller, something like:
declare
str_sql varchar2(200);
p_sql_o varchar2(4000);
begin
str_sql := get(p_sql_o);
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 5
Note that the line number this is reporting is 5, not 6 as you had; it's the line in the caller that has the error, not the line the assignment happens inside the function. If I make it str_sql varchar2(250); in the caller declaration then it works (but it might as well be 4000 to match the declaration in the function).
declare
str_sql varchar2(250);
p_sql_o varchar2(4000);
begin
str_sql := get(p_sql_o);
end;
/
PL/SQL procedure successfully completed.
But p_sql_o will still be empty because you never set it. It looks like the OUT parameter is redundant. Perhaps you meant to put the SQL string into that instead, and that is already declared as big enough in the caller; but then it isn't clear what the return value would be - you probably just want to remove the OUT parameter completely, and make sure the variable in the caller that you're putting the return value into is large enough.
Have you tried just running the SQL script against the DB outside of the SP? This will help to determine if the sql syntax is correct and that you are returning what is expected. It looks like you expect to get a response that is 4000 bytes or smaller. Are you sure that is what is being returned? I don't see Where clause. Maybe you are returning multiple rows that exceed your limit? Admittedly, I'm not as familiar with stored procedures, so I could be off.
Here you are using p_sql_o as out parameter and not assigning any value inside your function.
So p_sql_o will hold the value it had previously in the code follows after calling line of this function.
As you defined str_sql can hold only 4000 char, if your concatenated string is going beyond that limit it will give character string buffer too small ORA-06512 error.
When you are concatenating the string to create a select statement, check if you are using the single quotes properly or you are missing them at all. If this is happening then while executing the dynamic select statement you may get this error ORA-06502: PL/SQL: numeric or value error:
In Oracle, the full text search syntax of Contains operator is:
CONTAINS(
[schema.]column,
text_query VARCHAR2
[,label NUMBER]) RETURN NUMBER;
which means the text_query can not be more than 4000 characters long or an error will occur. I repeatedly have text_query longer than 4000 characters long in many cases. How would you, as an Oracle expert, suggest to get around such limitation if possible?
To further clarify the situation in which 4000 is easily reached is that if you combine many Contains Query Operators to construct your text_query, it is quite possible to exceed such 4000 characters limitation.
The 4000 character limit is not some arbitrary boundary: it is the maximum amount of VARCHAR2 characters that Oracle SQL can handle.
4000 characters is a lot of text. In English it's around 600 words, or an A4 page and a bit in a reasonable point font. There are not many applications I can think of which require searching for such large chunks of verbiage. Even colleges checking students' essays for plagiarism would operate at no more than the paragraph level.
However, if you really have a situation in which matching on a scant 4000 characters generates false positives all you can do is split the query string into chunks and search on them. This means you have to use PL/SQL:
create or replace function big_search (p_search_text in varchar2)
return sys_refcursor
is
return_value sys_refcursor;
p_srch1 varchar2(4000);
p_srch2 varchar2(4000);
begin
dbms_output.put_line('search_length='||to_char(length(p_search_text)));
p_srch1 := substr(p_search_text, 1, 4000);
p_srch2 := substr(p_search_text, 4001, 4000);
open return_value for
select docname
, (score(1) + score(2))/2 as score
from t23
where contains ( text_column, p_srch1 , 1) != 0
and contains ( text_column, p_srch2 , 2) != 0;
return return_value;
end;
/
If you don't know the size of the search text beforehand, then you'll need to use dynamic SQL to assemble this. Note that passing null search terms to CONTAINS() will hurl DRG-50901: text query parser syntax error.
The current version supports now a CLOB parameter
CONTAINS(
[schema.]column,
text_query [VARCHAR2|CLOB]
[,label NUMBER])
RETURN NUMBER;
http://docs.oracle.com/cd/B28359_01/text.111/b28304/csql.htm#i997503