While I try to set the value of over 4000 characters on a field that has data type CLOB, it gives me this error :
ORA-01704: string literal too long.
Any suggestion, which data type would be applicable for me if I have to set value of unlimited characters although for my case, it happens to be of about 15000 chars.
Note : The long string that I am trying to store is encoded in ANSI.
What are you using when operate with CLOB?
In all events you can do it with PL/SQL
DECLARE
str varchar2(32767);
BEGIN
str := 'Very-very-...-very-very-very-very-very-very long string value';
update t1 set col1 = str;
END;
/
Proof link on SQLFiddle
Try to split the characters into multiple chunks like the query below and try:
Insert into table (clob_column) values ( to_clob( 'chunk 1' ) || to_clob( 'chunk 2' ) );
It worked for me.
To solve this issue on my side, I had to use a combo of what was already proposed there
DECLARE
chunk1 CLOB; chunk2 CLOB; chunk3 CLOB;
BEGIN
chunk1 := 'very long literal part 1';
chunk2 := 'very long literal part 2';
chunk3 := 'very long literal part 3';
INSERT INTO table (MY_CLOB)
SELECT ( chunk1 || chunk2 || chunk3 ) FROM dual;
END;
Hope this helps.
The split work until 4000 chars depending on the characters that you are inserting. If you are inserting special characters it can fail.
The only secure way is to declare a variable.
Though its a very old question but i think sharing experience still might help others:
Large text can be saved in a single query if we break-down it in chunks of 4000 bytes/characters by concatinating them using '||'
Running following query will tell you:
Required Number of chunks containing 4000 bytes
Remaining bytes
Since, in given example you are trying to save text contining 15000 bytes (characters), so,
select 15000/4000 chunk,mod(15000,4000) remaining_bytes from dual;
Result:
That means, you need to concatenate 3 chunks of 4000 bytes and one chunk of 3000 bytes, so it would be like:
INSERT INTO <YOUR_TABLE>
VALUES (TO_CLOB('<1st_4K_bytes>') ||
TO_CLOB('<2nd_4K_bytes>') ||
TO_CLOB('<3rd_4K_bytes>') ||
TO_CLOB('<last_3K_bytes>)');
create a function that return a clob
create function ret_long_chars return clob is
begin
return to_clob('put here long characters');
end;
update table set column = ret_long_chars;
INSERT INTO table(clob_column) SELECT TO_CLOB(q'[chunk1]') || TO_CLOB(q'[chunk2]') ||
TO_CLOB(q'[chunk3]') || TO_CLOB(q'[chunk4]') FROM DUAL;
Accepted answer did not work for me in sql developper but combination of this answer and another one did :
DECLARE
str varchar2(32767);
BEGIN
update table set column = to_clob('Very-very-...-very-very-very-very-very-very long string value');
END;
/
Related
Can you advice on a method of doing updates on clob fields in Oracle?
The query is a simple one and running it I get
ORA-01704: string literal too long:
Update table_name
Set clob_field=value2
Where column=condition1 and clob_field=value1
The scope is to update the value in a clob column with a new value.
Thanks
Is Your code part of some PLSql procedure or it is simple SQL statement? Do You pass variable "value2" as bind variable or it is quoted string inside the query? Are You on 12c or some earlier release of Oracle DB?
Generally, the most common "not obvious" issue is related to the fact that varchar2 type is limited to 4000 characters in SQL statements. If You are in PLSql procedure, the limit is 32K characters.
Can You provide code sample? The fact is that following two statements result in different behavior:
update table_name
set clob_field=value2
where column=condition1
and clob_field=value1
update table_name
set clob_field='Some very long string.....end of very long string'
where column=condition1
and clob_field='Some even longer string.....end of even longer string'
Take a look at post Error : ORA-01704: string literal too long - example how to put update in plsql block in order to achieve 32.767 character limit.
edit:
Take a look at post Working with very large text data and CLOB column, too
As you may know, it is not possible to insert more than 4k characters at one time in a clob field with Oracle Database.
A workaround to solve this issue is to split the whole string in 2 string < 4k
Example :
create table t_test (id number, texte clob);
insert into t_test (id, texte) values(1, to_clob ('value of 3999 characters') || to_clob ('value of remaining 1001 characters'));
You can use the "Lorem ipsum" to do the test :-)
Put your string value to a CLOB variable first:
declare
c clob;
s varchar2(4000);
begin
s:=rpad('x',4000,'x');
for i in 1..100 loop
c:=c||s;
end loop;
dbms_output.put_line( dbms_lob.getlength(c) );
-- length of "c" is 400000 now
update table_name set clob_field=c where id=12345;
end;
/
I have a query of more than 4000 characters which is formed from different varaibles having varchar2 datatype of size 2000
example
query1 varcahr2(2000):='string 1';
query2 varchar2(2000):='string2';
query3 varcahr2 (2000):= string3';
I have declared a variable query varchar2(32000)
query := query1|| query2 || query3 ;
create table t (
id number,
querystring varchar2(4000));
I tried to get the first 4000 characters from the query variable it is not working. Can anyone please help?
declare
querystring1 varchar2(2000) := "string1";
querystring2 varchar2(2000) := "string2";
l_query varchar2(32000);
query varchar2(4000);
begin
l_query := querystring1 || querystring2 ;
select substr(l_query,1,4000) into query from dual;
insert into lib_query_table values('1',query);
end;
You are using double quotes around your query string literals, instead of single quotes. That makes Oracle interpret them as identifier names - so as soon as your literal is more than 30 characters long you'll get that exception. With shorter strings you'd still get an error, but something like 'unknown identifier'.
Replace your double quotes with single ones:
declare
querystring1 varchar2(2000) := 'string1';
querystring2 varchar2(2000) := 'string2';
l_query varchar2(32000);
query varchar2(4000);
begin
l_query := querystring1 || querystring2 ;
select substr(l_query,1,4000) into query from dual;
insert into lib_query_table values (1, query);
end;
You don't need the query from dual, you can do:
query := substr(l_query, 1, 4000);
You could skip that variable and do:
insert into lib_query_table (id, querystring)
values (1, substr(l_query, 1, 4000);
or even:
insert into lib_query_table (id, querystring)
values (1, substr(querystring1 || querystring2, 1, 4000));
As your ID column is a number you should not insert the value for that as a string '1' - just use a number. You probably want a sequence to set that value, eventually.
Also not directly related, but when you're concatenating parts of a query together, say where one string is the select list and the second is the from clause etc., make sure you have whitespace at the end of each part (or the start of the next part), or the combined string might end up invalid.
Variable-length character string having maximum length size bytes or characters. You must specify size for VARCHAR2. Minimum size is 1 byte or 1 character. Maximum size is: 32767 bytes or characters if MAX_STRING_SIZE = EXTENDED 4000 bytes or characters .
You can look CLOB data type
My problem is quite nontrivial.
I should execute PL/SQL script which runs INSERT INTO query. It looks like:
DECLARE
newId NUMBER(38,0) := &1;
BEGIN
Insert into FOO ("ID", "DESCRIPTION")
values (newId+1, 'LARGE CLOB WHICH PRODUCES EXCEPTION');
-- A LOT OF INSERT QUERIES
END;
/
exit;
So, I found that it's a good idea to assign CLOB to VARCHAR2 variable as It could be 32767 bytes long. My goal is to do that for each INSERT INTO query. Like:
--assign CLOB to VARCHAR2 variable
-- INSERT variable instead of CLOB type
I want to point out that I have a lot of INSERT INTO queries in script, so I should reassign variable before each INSERT INTO query, how can I do this?
You're getting the ORA-01704 because your string literal is more than 4000 bytes, which is the size limit for string literals in SQL calls. In PL/SQL the limit is 32k, so if all your values are less than that you can assign them to a PL/SQL variable and use that for the insert:
DECLARE
newId NUMBER(38,0) := &1;
newDescription varchar2(32767); -- or clob
BEGIN
newDescription := 'LARGE CLOB WHICH PRODUCES EXCEPTION';
Insert into FOO ("ID", "DESCRIPTION")
values (newId+1, newDescription);
newDescription := 'ANOTHER LARGE CLOB WHICH PRODUCES EXCEPTION';
Insert into FOO ("ID", "DESCRIPTION")
values (newId+1, newDescription);
...
END;
/
If any of the values are more than 32k you'll need a PL/SQL CLOB variable, and will need to construct that by appending shorted (<32k) string literals, which is messy.
Using multiple insert statements may not be the best way to go anyway. You might be able to use SQL*Loader or an external table to load the data more simply. Or you could read the values using utl_file, e.g. into the same PL/SQL variable, and then insert in a loop - which would be less code and easier to maintain.
You could also use a collection to hold the string values:
DECLARE
TYPE stringTab IS table of varchar2(32767); -- or clob
newDescriptions stringTab := new stringTab();
BEGIN
newDescriptions.extend;
newDescriptions(newDescriptions.last) := 'LARGE CLOB WHICH PRODUCES EXCEPTION';
newDescriptions.extend;
newDescriptions(newDescriptions.last) := 'ANOTHER LARGE CLOB WHICH PRODUCES EXCEPTION';
forall i in newDescriptions.first..newDescriptions.last
insert into FOO ("ID", "DESCRIPTION")
values (&1 + 1, newDescriptions(i));
END;
/
... which will be a trade-off between performance and (maybe) readability, against memory usage by the collection. And you can populate that in the block, or again read the values into the collection from a file, if that's feasible for your situation.
You can still generate this from queries against an existing table, with something like:
set pages 0
set lines 32767
set long 32767
set define off
select 'DECLARE' || chr(10)
|| ' newId NUMBER(38,0) := &1;' || chr(10)
|| ' newDescription varchar2(32767);' || chr(10)
|| 'BEGIN'
from dual;
select ' newDescription := q''[' || description || ']'';' || chr(10)
|| ' newId := newId + 1;' || chr(10)
|| ' insert into FOO ("ID", "DESCRIPTION") values (newId, newDescription);' || chr(10)
from foo;
select 'END;' || chr(10)
|| '/' || chr(10)
|| 'exit'
from dual;
set define on
I've used the alternative quoting mechanism in case any of your string values contain single quotes, but you'll need to pick a suitable quote delimiter. And again this assumes none of your CLOB values exceeds 32k.
I'd also reconsider if you really want to do this with a script full of insert statements; if the data is coming from a table anyway then an export/import might be more appropriate.
You can also declare a clob variable in PL/SQL. See the lob semantics and the DBMS_LOB package to manipulate it. It can be something like:
DECLARE
myLOBFromDatabase CLOB;
BEGIN
SELECT lobComun INTO myLOBFromDatabase FROM table WHERE id=1;
/* Manipulate lob wth dbms_lob package at will here */
Insert into FOO ("ID", "DESCRIPTION")
values (newId+1, myLOBFromDatabase );
END;
I have a little strange resolve of this, but it works.
DECLARE
JS CLOB;
BEGIN
JS := TO_CLOB( 'THE FIRST PART OF CLOB SHOULD BE LESS THEN 32K
......
......'||
'.......
THIS IS SECOND PART IS LESS THEN 32K TOO'||
'.......
LAST PART');
END;
This way got me to insert more than 1M
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 have a field in a table which holds XML entities for special characters, since the table is in latin-1.
E.g. "Hallöle slovenčina" (the "ö" is in latin-1, but the "č" in "slovenčina" had to be converted to an entity by some application that stores the values into the database)
Now I need to export the table into a utf-8 encoded file by converting the XML entities to their original characters.
Is there a function in Oracle that might handle this for me, or do I really need to create a huge key/value map for that?
Any help is greatly appreciated.
EDIT: I found the function DBMS_XMLGEN.convert, but it only works on <,> and &. Not on &#NNN; :-(
I believe the problem with dbms_xmlgen is that there are technically only five XML entities. Your example has a numeric HTML entity, which corresponds with Unicode:
http://theorem.ca/~mvcorks/cgi-bin/unicode.pl.cgi?start=0100&end=017F
Oracle has a function UNISTR which is helpful here:
select unistr('sloven\010dina') from dual;
I've converted 269 to its hex equivalent 010d in the example above (in Unicode it is U+010D). However, you could pass a decimal number and do a conversion like this:
select unistr('sloven\' || replace(to_char(269, 'xxx'), ' ', '0') || 'ina') from dual;
EDIT: The PL/SQL solution:
Here's an example I've rigged up for you. This should loop over and replace any occurrences for each row you select out of your table(s).
create table html_entities (
id NUMBER(3),
text_row VARCHAR2(100)
);
INSERT INTO html_entities
VALUES (1, 'Hallöle slovenčina Ċ ú');
INSERT INTO html_entities
VALUES (2, 'I like the letter Ċ');
INSERT INTO html_entities
VALUES (3, 'Nothing to change here.');
DECLARE
v_replace_str NVARCHAR2(1000);
v_fh UTL_FILE.FILE_TYPE;
BEGIN
--v_fh := utl_file.fopen_nchar(LOCATION IN VARCHAR2, FILENAME IN VARCHAR2, OPEN_MODE IN VARCHAR2, MAX_LINESIZE IN BINARY_INTEGER);
FOR v_rec IN (select id, text_row from html_entities) LOOP
v_replace_str := v_rec.text_row;
WHILE (REGEXP_INSTR(v_replace_str, '&#[0-9]+;') <> 0) LOOP
v_replace_str := REGEXP_REPLACE(
v_replace_str,
'&#([0-9]+);',
unistr('\' || replace(to_char(to_number(regexp_replace(v_replace_str, '.*?&#([0-9]+);.*$', '\1')), 'xxx'), ' ', '0')),
1,
1
);
END LOOP;
-- utl_file.put_line_nchar(v_fh, v_replace_str);
dbms_output.put_line(v_replace_str);
END LOOP;
--utl_file.fclose(v_fh);
END;
/
Notice that I've stubbed in calls to the UTL_FILE function to write NVARCHAR lines (Oracle's extended character set) to a file on the database server. The dbms_output, while great for debugging, doesn't seem to support extended characters, but this shouldn't be a problem if you use UTL_FILE to write to a file. Here's the DBMS_OUTPUT:
Hallöle slovencina C ú
I like the letter C
Nothing to change here.
You can also just use the internationalization package :
UTL_I18N.unescape_reference ('text')
Works great in changing those html entities to normal characters (such as cleanup after moving a database from iso 8859P1 to UTF-8)
This should probably be done in PL/SQL which I do not know, but I wanted to see how far I could get it with pure SQL. This only replaces the first occurence of the code, so you would have to somehow run it multiple times.
select regexp_replace(s, '&#([0-9]+);', u) from
(select s, unistr('\0' || REPLACE(TO_CHAR(TO_NUMBER(c), 'xxxx'), ' ', '')) u from
(select s, regexp_replace(s, '.*&#([0-9]+);.*', '\1') c from
(select 'Hallöle slovenčina' s from dual)))
Or less readable but more usable:
SELECT
REGEXP_REPLACE(s, '&#([0-9]+);', unistr('\0' || REPLACE(TO_CHAR(TO_NUMBER(regexp_replace(s, '.*?&#([0-9]+);.*$', '\1', 1, 1)), 'xxxx'), ' ', '')), 1, 1)
FROM
(SELECT 'Hallöle slovenčina č Ė' s FROM DUAL)
This (updated) version correctly replaces the first occurrence. You need to apply it until all of them are replaced.