ORA-06502: PL/SQL: numeric or value error: character string buffer too small only three numbers - sql

create or replace FUNCTION "FNC_CALCULATE_MOD11" (P_VALOR IN NUMBER)
return number is
Result number;
begin
DECLARE
-- LOCAL VARIABLES HERE
V_PROCESSO VARCHAR2(30);
V_PESO NUMBER := 2;
V_SOMA NUMBER := 0;
V_RESTO NUMBER := 0;
BEGIN
V_PROCESSO := TO_CHAR(P_VALOR);
WHILE LENGTH(V_PROCESSO) < 6 --Popular com zeros no inicio até 6
LOOP
V_PROCESSO := '0'||V_PROCESSO;
END LOOP;
--accuses error on this line
FOR I IN REVERSE 1 .. LENGTH(V_PROCESSO)
LOOP
V_SOMA := TO_CHAR (V_SOMA) + TO_NUMBER(SUBSTR(V_PROCESSO,i,1))*V_PESO;
IF V_PESO = 9 THEN --repetir peso se for necessario
V_PESO := 2;
ELSE
V_PESO := V_PESO + 1;
END IF;
END LOOP;
V_RESTO := MOD(V_SOMA, 11);
Result := 11 - V_RESTO;
IF ((Result = 0) OR (Result = 1) OR (Result >= 10)) THEN
Result := 1;
END IF;
END;
return(Result);
end FNC_CALCULATE_MOD11;

Try to change V_PROCESSO to a bigger size, for example V_PROCESSO VARCHAR2(300);

Related

Oracle how to instr for tab or end of line using regexp_instr

I have the following code
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
l_field_start := l_field_end + 1;
exit when l_field_end = 0;
end loop;
dbms_output.put_line('');
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;
with which I'm trying to parse this clob test data:
LINE_TEXT
"PERSON_ID_NUMBER 30000 1223445454"
"PERSON_DOB 30000 01-01-1900"
The clob data is tab separated and has a chr(10) at the end. I'm not familiar with regexp_instr, but currently I'm only using instr to search for the tab separators; so it's missing the end of line field and producing:
PERSON_ID_NUMBER,30000,,
PERSON_DOB,30000,,
How can I change the instr into a regexp_instr to also look for the end of line character in addition to the tab and then correctly pick up the last field?
I need the function to be performant, since it is parsing large files.
You can split the line of CLOB column by converting to char, and then apply regexp_substr() as
with t as
(
select level as row_num, regexp_substr(to_char(t.line_text),'^.*$',1,level,'m') as str
from tab t
connect by level <= length (to_char(t.line_text))
- length (replace (to_char(t.line_text), chr (10))) + 1
)
select row_num, regexp_replace(str,'[[:space:]]+',',') as str
from t;
ROW_NUM STR
------- -----------------------------------------
1 PERSON_ID_NUMBER,30000,1223445454
2 PERSON_DOB,30000,01-01-1900
Demo
Edit : even works without to_char() conversion, if your CLOB is huge then you need to split step by step by substr(str,1,4000), substr(str,4001,8000) ...
with t as
(
select level as row_num, regexp_substr(substr(t.line_text,1,4000),'^.*$',1,level,'m') str
from tab t
connect by level <= length (substr(t.line_text,1,4000))
- length (replace(substr(t.line_text,1,4000), chr (10))) + 1
)
select row_num, regexp_replace(substr(str,1,4000),'[[:space:]]+',',') as str
from t
Fixed it with:
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
exit when l_field_end = 0;
l_field_start := l_field_end + 1;
end loop;
l_field := substr(l_line, l_field_start);
dbms_output.put_line(l_field);
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;

PL-00215: varchar2 length error

I'm trying to do a very simple thing: extract a date and do some controls on it.
Everything's fine but when I try to compile this error pops out and I really don't have a clue about how to solve it...
This is the code:
function get_crediti_formativi_biennio(p_userid varchar2,
p_prof_abil varchar2,
p_id_persona number,
p_tipo_formazione number,
p_anno_formazione varchar2,
p_flag_calcolo number,
p_crediti_totali out number)
return varchar2 is
v_count number;
v_anno varchar2;
v_biennio_from number;
v_biennio_to number;
l_err_msg varchar2(1024) := '+OK0000 Operazione effettuata con successo';
begin
v_count := null;
begin
select TO_CHAR(ap.valore)
into v_anno
from intc_attr_persone ap
where ap.id_attributo = 202
and ap.id = p_id_persona;
exception
when no_data_found then
v_anno := '';
end;
if v_anno >= 2015 and (mod(v_anno, 2) != 0) then
if (mod(p_anno_formazione, 2) = 0) then
v_biennio_from := p_anno_formazione;
v_biennio_to := p_anno_formazione + 1;
else
v_biennio_from := p_anno_formazione - 1;
v_biennio_to := p_anno_formazione;
end if;
else
if v_anno < 2015 or v_anno = '' or v_anno is null then
if (mod(p_anno_formazione, 2) = 0) then
v_biennio_from := p_anno_formazione - 1;
v_biennio_to := p_anno_formazione;
else
v_biennio_from := p_anno_formazione;
v_biennio_to := p_anno_formazione + 1;
end if;
end if;
end if;
The problem is on v_anno.....

Unscramble string code in sql

I have a function in Oracle SQL which scrambles a given input. I need to unscramble it.
I need a program which can basically reverse the output of the program below.
source IN VARCHAR2
sGARBLED VARCHAR2(510);
index NUMBER;
length NUMBER;
onec NUMBER;
BEGIN
length := LENGTH(source);
IF length > 255 THEN
length := 255;
END IF;
index := 1;
sGARBLED := '';
WHILE index <= length LOOP
onec := ASCII(SUBSTR(source, index, 1)) - 30;
IF (onec < 10) THEN
sGARBLED := sGARBLED || '0';
END IF;
sGARBLED := sGARBLED || CAST(onec AS VARCHAR2);
index := index + 1;
END LOOP;
RETURN sGARBLED;
END
It can be possible, if you are only allowing character values in your original string before scrambling it, to be characters of the alphabet.
Code would look something like this:
sgarbled in VARCHAR2
UNsGARBLED VARCHAR2(255);
length NUMBER;
index NUMBER;
onec NUMBER;
BEGIN
length := LENGTH(sgarbled);
index := 1;
UNsGARBLED := '';
WHILE index <= length LOOP
onec := SUBSTR(sgarbled, index, 1);
IF (onec = '0') THEN
onec := SUBSTR(sgarbled, (index + 1), 1) + 30;
ELSE
onec := SUBSTR(sgarbled, (index), 2) + 30;
END IF;
UNsGARBLED := UNsGARBLED || CAST(CHR(onec) AS VARCHAR2);
index := index + 2;
END LOOP;
RETURN UNsGARBLED;
END
As Mr. Llama pointed out if you are allowing the original input string to contain all ASCII characters then it may not be possible.

Need to Write CLOB file data into ORACLE table with PLSQL

I have flat file inside a clob field and structure of the flat-file something like as below. and flat files containx million of records.
col1,col2,col3,col4,col5,col6
A,B,C,,F,D
1,A,2,B,B,C
Traditional ways I can't use like
1-fetch the data from clob in excel or something and the load the data into table with sql-loader.
2-currently I am able to print clob file with below code.
OPEN c_clob;
LOOP
FETCH c_clob INTO c;
EXIT
WHEN c_clob%notfound;
printout(c);
but problem in above code is if I use this variable into insert statement then it gives error due to CLOB to VAR insertion.
INSERT INTO Table1 VALUES(c);
commit;
ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: 848239, maximum: 4000)
Is there any other option available to handle huge flat-file from clob field and dump into a table.
Currently I am using below code
declare
nStartIndex number := 1;
nEndIndex number := 1;
nLineIndex number := 0;
vLine varchar2(2000);
cursor c_clob is
select char_data from clob_table where seq=1022;
c clob;
procedure printout
(p_clob in out nocopy clob) is
offset number := 1;
amount number := 32767;
amount_last number := 0;
len number := dbms_lob.getlength(p_clob);
lc_buffer varchar2(32767);
line_seq pls_integer := 1;
-- For UNIX type file - replace CHR(13) to NULL
CR char := chr(13);
--CR char := NULL;
LF char := chr(10);
nCRLF number;
sCRLF varchar2(2);
b_finish boolean := true;
begin
sCRLF := CR || LF;
nCRLF := Length(sCRLF);
if ( dbms_lob.isopen(p_clob) != 1 ) then
dbms_lob.open(p_clob, 0);
end if;
amount := instr(p_clob, sCRLF, offset);
while ( offset < len )
loop
-- For without CR/LF on end file
If amount < 0 then
amount := len - offset + 1;
b_finish := false;
End If;
dbms_lob.read(p_clob, amount, offset, lc_buffer);
If b_finish then
lc_buffer := SUBSTR(lc_buffer,1,Length(lc_buffer)-1);
End If;
if (line_seq-1) > 0 then
amount_last := amount_last + amount;
offset := offset + amount;
else
amount_last := amount;
offset := amount + nCRLF;
end if;
amount := instr(p_clob, sCRLF, offset);
amount := amount - amount_last;
dbms_output.put_line('Line #'||line_seq||': '||lc_buffer);
line_seq := line_seq + 1;
end loop;
if ( dbms_lob.isopen(p_clob) = 1 ) then
dbms_lob.close(p_clob);
end if;
exception
when others then
dbms_output.put_line('Error : '||sqlerrm);
end printout;
begin
open c_clob;
loop
fetch c_clob into c;
exit when c_clob%notfound;
printout(c);
end loop;
close c_clob;
end;
Here line printout(c); (4th last line in code) showing me clob data line by line untill buffer gets overflow.
Expected result: To read data from clob flat-file and insert rows into table column wise, That's I am trying to achieve. Constraints is Flat-Files contains millions of records.
i'm using something like this
...
select * into ifile from clob_table where ...;
file_length := dbms_lob.getlength (ifile.char_data);
p_start :=1;
while p_start<>0 loop
end_pos := dbms_lob.instr (ifile.char_data, chr (10), p_start);
if end_pos > 0 then
strRow := dbms_lob.substr(ifile.char_data, least (end_pos - p_start, 240), p_start),chr (13)||chr (10);
p_start := end_pos + 1;
tabRow := strRow2tabRow(strRow);
else
strRow := dbms_lob.substr (ifile.char_data, file_length - p_start + 1, p_start);
p_start := 0;
tabRow := strRow2tabRow(strRow);
end if;
insert into myTable values tabRow;
end loop;
...
and functions
function strRow2tabRow(strRow varchar2) return myTable%rowtype is
tabRow myTable%rowtype;
begin
tabRow.col1:=valueIncolumn(strRow,1);
tabRow.col2:=valueIncolumn(strRow,2);
...
/*or maybe this may be better for you
select * into tabRow from (
select rownum rn, regexp_substr(strRow,'[^,]+', 1, level) hdn from dual connect by regexp_substr(strRow, '[^,]+', 1, level) is not null
) pivot (max(hdn) for rn in (1, 2, ...));
*/
return tabRow;
exception when others then
return tabRow;
end;
function valueIncolumn(strRow varchar2, pos in number) return varchar2 is
ret varchar2(1024);
begin
select hdn into ret from (
select rownum rn, regexp_substr(strRow,'[^,]+', 1, level) hdn from dual connect by regexp_substr(strRow, '[^,]+', 1, level) is not null
) where rownum=pos;
return ret;
exception when others then
return null;
end;
hope it helps

PLSQL Printing prime numbers

I want to print prime numbers between 1 to 50. But I don't understand what I am doing wrong in my code. After BEGIN, SQLDeveloper says I had an error because it expected another sign and not = .
SET SERVEROUTPUT ON
DECLARE
i NUMBER;
counter NUMBER;
n NUMBER;
k NUMBER;
BEGIN
i := 2;
counter := 0;
n := 50;
k := n/2;
FOR i IN 1..k LOOP
IF (n%i := 0 ) THEN
counter := 1;
END IF;
IF (counter := 0) THEN
DBMS_OUTPUT.PUT_LINE(n||' is prime number');
END IF;
END LOOP;
END;
SET SERVEROUTPUT ON
DECLARE
i NUMBER;
counter NUMBER;
n NUMBER;
k NUMBER;
BEGIN
i := 2;
counter := 0;
n := 50;
k := floor(n/2);
FOR i IN 1..k LOOP
IF (mod(n, i) = 0 ) THEN
counter := 1;
END IF;
IF (counter = 0) THEN
DBMS_OUTPUT.PUT_LINE(n||' is prime number');
END IF;
END LOOP;
END;
k := n/2; -- added FLOOR (k is NUMBER, by default it's NUMBER(38, max_scale))
IF (n%i := 0 ) THEN -> IF (mod(n, i) = 0 ) THEN
Oracle has MOD function for remainder + to compare you need to use =,
:= is for assignment.
DECLARE
counter NUMBER;
k NUMBER;
BEGIN
FOR n IN 1..50 LOOP
counter := 0;
k := floor(n/2);
FOR i IN 2..k LOOP
IF (mod(n, i) = 0 ) THEN
counter := 1;
END IF;
END LOOP;
IF (counter = 0) THEN
DBMS_OUTPUT.PUT_LINE(n||' is prime number');
END IF;
END LOOP;
END;
In your IF clause you are assigning the value instead of comparison. You are using := operator, where you shd be using =
It shud be like (IF counter = 0) then
......
Also I dont think n%i would work, you could do IF (trunc(n) = n) then ... or IF (mod (n,i) =0) then ...
--this function is check prime number.
create or replace function prime_a(x number) return
varchar2 is
n integer;
ans varchar2(50);
begin
n:=(x/2);
for i in 2..n loop
if mod(x,i)=0
then ans:='not a prime';
exit;
else ans:='prime';
end if;
end loop;
return ans;
end;
/
step-1:
create table tob(prime number);
step-2:
create or replace procedure ro(m number,n number)
is
a integer;
co Boolean;
begin
for j in m..n loop
co:=false;
co:=(j=1 );
a:=(j/2);
for i in 2..a loop
co:=(mod(j,i)=0);
exit when co;
end loop;
if(not co) then
insert into tob values(J);
end if;
end loop;
commit;
end;
/
step-3:
exec ro(1,50);
step-4: check:-
select * from tob;
You should create or replace in your source code:
function prime_a(x number) return
varchar2 is
n integer;
ans varchar2(50);
begin
n:=(x/2);
for i in 2..n loop
if mod(x,i)=0
then ans:='not a prime';
exit;
else ans:='prime';
end if;
end loop;
return ans;
end;
/
Why don't you just check for previous prime divisibility?
create table prime (primeno bigint)
declare #counter bigint
set #counter = 2
while #counter < 1000000
begin
if not exists(select top 1 primeno from prime where #counter % primeno = 0)
insert into prime select #counter
set #counter = #counter + 1
end
select * from prime order by 1
You could certainly cap the numbers you are checking against in the where condition to reduce your overheads further.
declare
i number;
j number;
k number:=0;
begin
for i in 1..50
loop
for j in 2..i-1
loop
if mod(i,j)=0 then
k:=1;
exit;
end if;
end loop;
if k=0 then
dbms_output.put_line(i);
end if;
k:=0;
end loop;
end;
/
create or replace function prime_a(x number) return varchar2 is
n integer;
ans varchar2(50);
begin
n:=(x/2);
for i in 2..n loop
if mod(x,i)=0 then
ans:='not a prime';
exit;
else
ans:='prime';
end if;
end loop;
return ans;
end;
/