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
Related
I've a string with the values - 'a','b','c','d,'e'. I need to convert each of the values to the text null - null,null,null,null,null. If there are 10 values in quotes and separated by comma, then 10 null will appear. I tried using REGEXP_REPLACE but failed to get the result.
declare
a varchar2(32767) := q'#'a','b','c'#';
c varchar2(32767);
begin
c := replace(REGEXP_REPLACE (a, <don't know what pattern should be here>, 'null'),'''');
dbms_output.put_line(c);
end;
/
There are many ways to do this. For example, you could use the regular expression '[^,]+' - which means any number (one or more) of consecutive non-comma characters. Then every occurrence of that pattern will be replaced with the replacement string (while the commas will stay in place).
declare
a varchar2(32767) := q'#'a','b','c'#';
c varchar2(32767);
begin
c := REGEXP_REPLACE (a, '[^,]+', 'null'); -- notice the regular expression
dbms_output.put_line(c);
end;
/
null,null,null
PL/SQL procedure successfully completed.
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
Whenever the length of string l_long_string is above 4000 characters, the following code is throwing an error:
ORA-01460: unimplemented or unreasonable conversion requested
Instead of the nested regexp_substr query, when I try to use
SELECT column_value
FROM TABLE(l_string_coll)
it throws:
ORA-22905: cannot access rows from a non-nested table item
How can I modify the dynamic query?
Notes:
- l_string_coll is of type DBMS_SQL.VARCHAR2S, and comes as input to my procedure (here, i have just shown as an anonymous block)
- I'll have to manage without creating a User-defined Type in DB schema, so I am using the in-built DBMS_SQL.VARCHAR2S.
- This is not the actual business procedure, but is close to this. (Can't post the original)
- Dynamic query has to be there since I am using it for building the actual query with session, current application schema name etc.
/*
CREATE TABLE some_other_table
(word_id NUMBER(10), word_code VARCHAR2(30), word VARCHAR2(255));
INSERT INTO some_other_table VALUES (1, 'A', 'AB');
INSERT INTO some_other_table VALUES (2, 'B', 'BC');
INSERT INTO some_other_table VALUES (3, 'C', 'CD');
INSERT INTO some_other_table VALUES (4, 'D', 'DE');
COMMIT;
*/
DECLARE
l_word_count NUMBER(10) := 0;
l_counter NUMBER(10) := 0;
l_long_string VARCHAR2(30000) := NULL;
l_dyn_query VARCHAR2(30000) := NULL;
l_string_coll DBMS_SQL.VARCHAR2S;
BEGIN
-- l_string_coll of type DBMS_SQL.VARCHAR2S comes as Input to the procedure
FOR i IN 1 .. 4100
LOOP
l_counter := l_counter + 1;
l_string_coll(l_counter) := 'AB';
END LOOP;
-- Above input collection is concatenated into CSV string
FOR i IN l_string_coll.FIRST .. l_string_coll.LAST
LOOP
l_long_string := l_long_string || l_string_coll(i) || ', ';
END LOOP;
l_long_string := TRIM(',' FROM TRIM(l_long_string));
dbms_output.put_line('Length of l_long_string = ' || LENGTH(l_long_string));
/*
Some other tasks in PLSQL done successfully using the concatenated string l_long_string
*/
l_dyn_query := ' SELECT COUNT(*)
FROM some_other_table
WHERE word IN ( SELECT TRIM(REGEXP_SUBSTR(str, ''[^,]+'', 1, LEVEL)) word
FROM ( SELECT :string str FROM SYS.DUAL )
CONNECT BY TRIM(REGEXP_SUBSTR(str, ''[^,]+'', 1, LEVEL)) IS NOT NULL )';
--WHERE word IN ( SELECT column_value FROM TABLE(l_string_coll) )';
EXECUTE IMMEDIATE l_dyn_query INTO l_word_count USING l_long_string;
dbms_output.put_line('Word Count = ' || l_word_count);
EXCEPTION
WHEN OTHERS
THEN
dbms_output.put_line('SQLERRM = ' || SQLERRM);
dbms_output.put_line('FORMAT_ERROR_BAKCTRACE = ' || dbms_utility.format_error_backtrace);
END;
/
How can I modify the dynamic query?
First of all. Based on the code you've provided, there is absolutely no need to use dynamic, native or DBMS_SQL dynamic SQL at all.
Secondly, SQL cannot operate on "strings" that are greater than 4K bytes in length(Oracle versions prior to 12c), or 32K bytes(Oracle version 12cR1 and up, if MAX_STRING_SIZE initialization parameter is set to EXTENDED).
PL/SQL, on the other hand, allows you to work with varchar2() character strings that are greater than 4K bytes (up to 32Kb) in length. If you just need to count words in a comma separated sting, you can simply use regexp_count() regular expression function(Oracle 11gr1 and up) as follows:
set serveroutput on;
set feedback off;
clear screen;
declare
l_str varchar2(100) := 'aaa,bb,ccc,yyy';
l_numOfWords number;
begin
l_numOfWords := regexp_count(l_str, '[^,]+');
dbms_output.put('Number of words: ');
dbms_output.put_line(to_char(l_numOfWords));
end;
Result:
Number of words: 4
My situation is quite strange, and i don't have any ideea on how to handle it. Scenario:
In variable v_tables_param I have the following string (the names of the tables that iwant to export) 'IN(''REPORT_PERIOD'',''OBJECT_AVAILABILITY'')'.
when i try to specify the following metadata filter that i need in order export the tables :
DBMS_DATAPUMP.METADATA_FILTER(handle => n_h1, name =>'NAME_EXPR',value =>v_tables_param);
i get a ORA-39001: invalid argument value.
However, if i hrad code the exact value of v_tables_param into the metadata filter, it works like a charm :
DBMS_DATAPUMP.METADATA_FILTER(handle => n_h1, name =>'NAME_EXPR',value =>'IN(''REPORT_PERIOD'',''OBJECT_AVAILABILITY'')');
Any idea what is happening here?
Are there some weird scenarios in oracle when a hard coded string is different from a variable that has the same value ?
EDIT: I added the function that computes the value of v_tables_param
FUNCTION SPLIT_TABLES(
v_tables_list VARCHAR2 --this is a string that looks like "table1,table2,table3"
) RETURN VARCHAR2
IS
n_idx PLS_INTEGER;
n_i PLS_INTEGER := 0;
v_tables VARCHAR2(2000) := v_tables_list;
v_filter_value VARCHAR(2000);
v_current_table VARCHAR2(200);
BEGIN
v_filter_value := '''IN(';
LOOP
n_idx := instr(v_tables,',');
IF n_idx > 0 THEN
v_current_table := (substr(v_tables,1,n_idx-1));
v_filter_value := v_filter_value || '''''' || v_current_table || ''''',';
v_tables := substr(v_tables,n_idx+1);
n_i := n_i + 1;
ELSE
v_current_table := v_tables;
v_filter_value := v_filter_value || '''''' || v_current_table || ''''')''';
EXIT;
END IF;
END LOOP;
RETURN v_filter_value;
END SPLIT_TABLES;
Ther're actually more parameters to metadata_filter (nice example of a doc bug):
SQL> desc dbms_datapump.metadata_filter
Parameter Type Mode Default?
----------- -------- ---- --------
HANDLE NUMBER IN
NAME VARCHAR2 IN
VALUE VARCHAR2 IN
OBJECT_PATH VARCHAR2 IN Y
OBJECT_TYPE VARCHAR2 IN Y
HANDLE NUMBER IN
NAME VARCHAR2 IN
VALUE CLOB IN
OBJECT_PATH VARCHAR2 IN Y
OBJECT_TYPE VARCHAR2 IN Y
...and I believe you'll have to qualify the object type you're filtering for:
object_type => 'TABLE'
update after you provided the function source:
Remove two apostrophes from each side of the filter values.
Double apostrophes are required by the compiler only. The value of the filter parameter must contain single apostrophes, but your function creates them in pairs.
your variable should contain only single quote
variable = 'IN('REPORT_PERIOD','OBJECT_AVAILABILITY')'
then, pls try , it works
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;
/