How to troubleshoot ORA-6502 / ORA-6512 errors? - oracle

I am running the below query.
declare
i int;
c_limit constant pls_integer default 10000;
TYPE cdr is table of TABLE_0.ENTITY_ID% type;
c_cdrt_tbl cdr;
cursor c_cdr is (select ENTITY_ID from TABLE_0 where TIME < 1577836800 and status = 2);
BEGIN
open c_cdr ;
LOOP
fetch c_cdr bulk collect into c_cdrt_tbl limit c_limit;
exit when c_cdrt_tbl.count =0;
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_1 where ID=c_cdrt_tbl(i);
delete from TABLE_2 where ID=c_cdrt_tbl(i);
delete from TABLE_3 where ID=c_cdrt_tbl(i);
delete from TABLE_4 where ID=c_cdrt_tbl(i);
delete from TABLE_5 where ID=c_cdrt_tbl(i);
delete from TABLE_6 where ID=c_cdrt_tbl(i);
commit;
END LOOP;
END;
/
Receiving the below error:
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: NULL index table key value
ORA-06512: at line 18
I am trying to perform cleanup on some tables which are related to one another via the ENTITY_ID (or ID) value. This value is of
type Number(38,0)
in all the mentioned tables.
There might be something wrong with the declaration of the initial variables, and the error suggests (at least to me) that the output of
select ENTITY_ID from TABLE_0 where TIME < 1577836800 and status = 2
is null, however it is not, I have double checked.
Note: Time is in epoch format.

You can do it all without cursor loops (and don't need PL/SQL):
DELETE FROM TABLE_1
WHERE id IN ( SELECT entity_id FROM table_0 WHERE time < 1577836800 AND status = 2 );
DELETE FROM TABLE_2
WHERE id IN ( SELECT entity_id FROM table_0 WHERE time < 1577836800 AND status = 2 );
DELETE FROM TABLE_3
WHERE id IN ( SELECT entity_id FROM table_0 WHERE time < 1577836800 AND status = 2 );
DELETE FROM TABLE_4
WHERE id IN ( SELECT entity_id FROM table_0 WHERE time < 1577836800 AND status = 2 );
DELETE FROM TABLE_5
WHERE id IN ( SELECT entity_id FROM table_0 WHERE time < 1577836800 AND status = 2 );
DELETE FROM TABLE_6
WHERE id IN ( SELECT entity_id FROM table_0 WHERE time < 1577836800 AND status = 2 );
COMMIT;
If you want to do it with a collection then:
DECLARE
v_ids SYS.ODCIVARCHAR2LIST;
BEGIN
SELECT entity_id
BULK COLLECT INTO v_ids
FROM table_0
WHERE time < 1577836800 AND status = 2;
DELETE FROM TABLE_1
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_2
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_3
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_4
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_5
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_6
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
COMMIT;
END;
/
or, if you wanted to use batches:
DECLARE
CURSOR c_rec IS
SELECT entity_id
FROM table_0
WHERE time < 1577836800 AND status = 2;
v_ids SYS.ODCIVARCHAR2LIST;
BEGIN
OPEN c_rec;
LOOP
EXIT WHEN c_rec%NOTFOUND;
FETCH c_rec BULK COLLECT INTO v_ids LIMIT 10000;
DELETE FROM TABLE_1
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_2
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_3
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_4
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_5
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
DELETE FROM TABLE_6
WHERE id IN ( SELECT COLUMN_VALUE FROM TABLE(v_ids) );
END LOOP;
COMMIT;
END;
/
If you want to fix yours then you need to use FORALL for each delete:
declare
c_limit constant pls_integer default 10000;
TYPE cdr is table of TABLE_0.ENTITY_ID% type;
c_cdrt_tbl cdr;
cursor c_cdr is
select ENTITY_ID
from TABLE_0
where TIME < 1577836800
and status = 2;
BEGIN
open c_cdr ;
LOOP
fetch c_cdr bulk collect into c_cdrt_tbl limit c_limit;
exit when c_cdrt_tbl.count =0;
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_1 where ID=c_cdrt_tbl(i);
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_2 where ID=c_cdrt_tbl(i);
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_3 where ID=c_cdrt_tbl(i);
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_4 where ID=c_cdrt_tbl(i);
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_5 where ID=c_cdrt_tbl(i);
FORALL i in 1..c_cdrt_tbl.count
delete from TABLE_6 where ID=c_cdrt_tbl(i);
commit;
END LOOP;
END;
/

Related

getting ORA-01422: exact fetch returns more than requested number of rows while calling a procedure

CREATE TABLE cmb_staging (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
insert into cmb_staging values(1,'A','AA',null,null);
insert into cmb_staging values(1,'B','BB',null,null);
insert into cmb_staging values(2,'C','CC',null,null);
insert into cmb_staging values(2,'D','DD',null,null);
insert into cmb_staging values(3,'A','AA',null,null);
CREATE TABLE cmb_target (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30)
);
CREATE TABLE cmb_reject (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
CREATE TABLE SUMMARY_TAB
( TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
PROCESS_STATUS VARCHAR2(30)
);
Procedure :
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
UPDATE cmb_staging
SET validation_status = 'Fail',
validation_result = CASE
WHEN e_id IS NULL
THEN 'Id is not present'
ELSE 'Id is longer than expected'
END
WHERE e_id is null
OR LENGTH(e_id) > 4;
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
if lv_count < 1 then
MERGE INTO cmb_target t
USING (SELECT e_id,
e_name,
e_loc
FROM cmb_staging
WHERE validation_status IS NULL) S
ON (t.e_id = S.e_id)
WHEN MATCHED THEN UPDATE SET
t.e_name = s.e_name,
t.e_loc = s.e_loc
WHEN NOT MATCHED THEN INSERT (t.e_id,t.e_name,t.e_loc)
VALUES (s.e_id,s.e_name,s.e_loc);
lv_succ_rec := SQL%ROWCOUNT;
else
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
end if;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
COMMIT;
ov_err_msg := 'Procedure completed succesfully';
EXCEPTION
WHEN OTHERS THEN
ov_err_msg := 'Procedure end up with errors'|| sqlerrm;
ROLLBACK;
END sp_dup_rec;
Calling a procedure :
set serveroutput on;
declare
v_err_msg varchar2(100);
begin
sp_dup_rec(v_err_msg);
dbms_output.put_line(v_err_msg);
end;
Hi Team,
I am getting ora-01422 error while calling a procedure. Basically, I want to insert duplicate records into the cmb_reject tab because the merge statement will fail and I will get ora - 30296 error if I will use only merge. SO, I have written if condition wherein it will fetch the count and if the count is more then will insert into cmb_reject tab
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
This raises the exception as you are trying to insert all 5 e_id values into a single variable and it raises a TOO_MANY_ROWS exception. (Apart from the fact that it is not identifying duplicates.)
Rather than trying to identify duplicates as a separate part of the process, you can do all the processing in the original UPDATE (in this case, converting it to a MERGE statement):
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
MERGE INTO cmb_staging dst
USING (
SELECT ROWID AS rid,
CASE
WHEN e_id IS NULL
THEN 'Id is not present'
WHEN LENGTH(e_id) > 4
THEN 'Id is longer than expected'
WHEN num_e_id > 1
THEN 'Duplicate IDs'
END AS failure_reason
FROM (
SELECT e_id,
COUNT(*) OVER (PARTITION BY e_id) AS num_e_id
FROM cmb_staging
)
WHERE e_id IS NULL
OR LENGTH(e_id) > 4
OR num_e_id > 1
) src
ON (src.rid = dst.ROWID)
WHEN MATCHED THEN
UPDATE
SET validation_status = 'Fail',
validation_result = failure_reason;
MERGE INTO cmb_target t
USING (
SELECT e_id,
e_name,
e_loc
FROM cmb_staging
WHERE validation_status IS NULL
) S
ON (t.e_id = S.e_id)
WHEN MATCHED THEN
UPDATE
SET t.e_name = s.e_name,
t.e_loc = s.e_loc
WHEN NOT MATCHED THEN
INSERT (t.e_id,t.e_name,t.e_loc)
VALUES (s.e_id,s.e_name,s.e_loc);
lv_succ_rec := SQL%ROWCOUNT;
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
COMMIT;
ov_err_msg := 'Procedure completed succesfully';
EXCEPTION
WHEN OTHERS THEN
ov_err_msg := 'Procedure end up with errors'|| sqlerrm;
ROLLBACK;
END sp_dup_rec;
/
db<>fiddle here
The error stack should tell you the line that is throwing the error. My guess is that the line is
select e_id
into lv_count
from cmb_staging;
because that query doesn't make sense. If there are multiple rows in cmb_staging, the query will throw the ORA-01422 error because you can't put multiple rows in a scalar variable. It seems highly probable that you'd at least want your query to do a count. And most likely with some sort of predicate. Something like
select count(*)
into lv_count
from cmb_staging;
would avoid the error. But that likely doesn't make sense given your comments where you say "it should go into the cmb_reject table" which implies that there is a singular object. This change would cause lv_count > 0 whenever there were any rows in cmb_staging and it seems unlikely that you want to always reject rows.
My rough guess is that you really mean "two rows with the same e_id" when you say "duplicate". If that's right
select e_id, count(*)
from cmb_staging
group by e_id
having count(*) > 1
will show you the duplicate e_id values. You could then
insert into cmb_reject
select s.*
from cmb_staging s
where e_id in (select s2.e_id
from cmb_staging s2
group by s2.e_id
having count(*) > 1);
If you don't really care about performance (I'm assuming based on the questions you've asked that you're just learning PL/SQL and you're not trying to manage a multi-TB data warehouse load) and just want to process the records in a loop
for stg in (select s.*,
count(*) over (partition by s.e_id) cnt
from cmb_staging s)
loop
if( stg.cnt > 1 )
then
insert into cmb_reject( e_id, e_name, e_loc )
values( stg.e_id, stg.e_name, stg.e_loc );
l_fail_cnt := l_fail_cnt + 1;
else
merge into cmb_target tgt
using( select stg.e_id e_id,
stg.e_name e_name,
stg.e_loc e_loc
from dual ) src
on( src.e_id = tgt.e_id )
when not matched
then
insert( e_id, e_name, e_loc )
values( src.e_id, src.e_name, src.e_loc )
when matched
then
update set e_name = src.e_name,
e_loc = src.e_loc;
l_success_cnt := l_success_cnt + 1;
end if;
end loop;
This is a problem:
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
As there's no WHERE clause which would restrict number of rows being returned, if cmb_staging contains 2 (or more) rows, query will fail because you can't put that many rows into a scalar lv_count variable.
Looks like you actually meant to say
select count(*) into lv_count from cmb_staging;
as the next line says
if lv_count < 1 then

Check if Exists PLS-00405: subquery not allowed in this context

I have cursor it selects from TableA then Fetch Loop that inserts into TableB.
I want to check if the value already exists in the TableB.
If it exists then I want to skip the insert.
create or replace
PROCEDURE DAILY_RPT (
v_start IN DATE,
v_end IN DATE)
IS
ao_out_no out_pair.out_no%type;
cursor get is
SELECT ao_out_no from tableA;
BEGIN
open get;
LOOP
fetch get into ao_out_no;
EXIT WHEN get%NOTFOUND;
if (ao_out_no = (select out_no from TableA where out_no = ao_out_no) THEN
--DO NOTHING
else
INSERT INTO TABLEB(OUT_NO) VALUES (ao_out_no);
end if;
END LOOP;
close get;
END;
I used IF CONDITION however, I used variable into if condition & I am getting below.
PLS-00405: subquery not allowed in this context
if (ao_out_no = (select out_no from TableA where out_no = ao_out_no) THEN
You don't need cursor or PL/SQL at all:
INSERT INTO TABLEB(OUT_NO)
SELECT ao_out_no
FROM tableA ta
WHERE ... -- filtering rows
AND NOT EXISTS (SELECT * From TableB tb WHERE tb.OUT_NO = ta.ao_out_no);
Use the following :
for i in (
select out_no from TableA where out_no
)
loop
if i.out_no = ao_out_no
then
-- DO NOTHING
else
...
or
create a new variable named x, and then assign a value to it by
select out_no into x from TableA where out_no = ao_out_no;
and check returning value for x.
With corrected syntax, it would be something like this:
create or replace procedure daily_rpt
( v_start in date
, v_end in date )
as
begin
for r in (
select ao_out_no, 0 as exists_check
from tablea
)
loop
select count(*) into exists_check
from tablea
where out_no = r.ao_out_no
and rownum = 1;
if r.exists_check > 0 then
--DO NOTHING
else
insert into tableb (out_no) values (r.ao_out_no);
end if;
end loop;
end;
However, it's inefficient to query all of the rows and then do an additional lookup for each row to decide whether you want to use it, as SQL can do that kind of thing for you. So version 2 might be something like:
create or replace procedure daily_rpt
( v_start in date
, v_end in date )
as
begin
for r in (
select ao_out_no
from tablea
where not exists
( select count(*)
from tablea
where out_no = r.ao_out_no
and rownum = 1 )
)
loop
insert into tableb (out_no) values (r.ao_out_no);
end loop;
end;
at which point you might replace the whole loop with an insert ... where not exists (...) statement.

Insert within FORALL loop in PL/SQL

Is it possible to do something like this in pl/sql for bulk insert using FORALL?
TYPE c_type1 IS RECORD
(
column1 table1.column1%TYPE,
column2 table1.column2%TYPE,
client table2.client%TYPE
);
type1 c_type1;
CURSOR cur_t IS select * BULK COLLECT INTO recs from table3 ;
begin
FOR recs IN cur_t
LOOP
SELECT * INTO type1 FROM (select a.column1, a.column2,imm.client
...
from table1 a, table2 imm
WHERE
a.column1 = recs.column1
) WHERE ROWNUM=1;
INSERT INTO table2 values (recs.column1,type1.column2);
...
P.S : There are more 80 columns to be inserted.
Your question is not pretty clear but looking at your code I have the following. Check if this is what you were looking for.
declare
CURSOR cur_t IS
select t3.column1 , t1.column2
from table3 t3
inner join table1 t1
on t3.column1 = t1.column1;
type var_cur is table of cur_t%rowtype;
var var_cur;
begin
open cur_t;
LOOP
FETCH cur_t bulk collect into var limit 100;
EXIT WHEN cur_t%NOTFOUND;
FORALL i IN 1 .. var.count SAVE EXCEPTIONS
INSERT INTO TABLE2
VALUES var(i);
END LOOP;
CLOSE distinctUserIdCursor;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error in Insertion of record' || '~~~~' || SQLERRM);
FOR indx IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
DBMS_OUTPUT.put_line (SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX|| ': '
|| SQL%BULK_EXCEPTIONS (indx).ERROR_CODE);
END LOOP;
end;

Oracle - Select a value in to a variable and use it in next select statement

Hi Every one I am trying to Select a value in to a variable and use it in next select statement but it is not working can anyone help me plz... is it possible to do so?
I am using oracle 10.5 with Toad
variable v_NextID int;
begin
begin
SELECT NOTE_ID INTO v_NextID FROM (
SELECT NOTE_ID,row_number() OVER (ORDER BY NOTE_ID ASC ) AS row_num
FROM tableName WHERE ID ='838'
) t
WHERE row_num = 2;
end;
select v_NextID, ID from tableName WHERE ID ='838';
end
declare
v_NextID number;
begin
SELECT NOTE_ID INTO v_NextID FROM (
SELECT NOTE_ID,row_number() OVER (ORDER BY NOTE_ID ASC ) AS row_num
FROM tableName WHERE ID ='838'
) t
WHERE row_num = 2;
select v_NextID, ID
into ... -- here you need to add INTO clause
from tableName WHERE ID ='838';
end;

Not able to compile PL/SQL with BULK COLLECT and FORALL

I am getting below error while creating this procedure.
CREATE OR replace PROCEDURE Remove_sv_duplicate
IS
TYPE sv_bulk_collect
IS TABLE OF tt%ROWTYPE;
sv_rec SV_BULK_COLLECT;
CURSOR cur_data IS
SELECT *
FROM tt
WHERE ROWID IN (SELECT ROWID
FROM (SELECT ROWID,
Row_number () over (PARTITION BY portingtn,
nnsp
, onsp,
spid,
Trunc(
createddate,
'MI') ORDER BY portingtn) dup
FROM tt)
WHERE dup > 1);
BEGIN
OPEN cur_data;
LOOP
FETCH cur_data BULK COLLECT INTO sv_rec LIMIT 1000;
FORALL i IN 1..sv_rec.COUNT
INSERT INTO soa_temp_sv_refkey_fordelete
(referencekey,
spid,
nnsp,
onsp,
portingtn)
(SELECT referencekey,
spid,
nnsp,
onsp,
portingtn
FROM tt
WHERE portingtn = Sv_rec(i).portingtn
AND spid = Sv_rec(i).spid
AND nnsp = Sv_rec(i).nnsp
AND onsp = Sv_rec(i).onsp
AND svid IS NULL);
EXIT WHEN cur_data%notfound;
END LOOP;
CLOSE cur_data;
COMMIT;
END;
Procedure
Error(23,5): PL/SQL: SQL Statement ignored
Error(25,27): PLS-00382: expression is of wrong type
Error(25,27): PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND table of records
Error(26,27): PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND table of records
Error(26,27): PLS-00382: expression is of wrong type
Error(27,27): PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND table of records
Error(27,27): PLS-00382: expression is of wrong type
Error(28,27): PL/SQL: ORA-22806: not an object or REF
Error(28,27): PLS-00382: expression is of wrong type
Error(28,27): PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND table of records
You cannot* reference individual fields when you're using FORALL - that's why you get the PLS-00436 error.
To get around this, you will have to make use of associative arrays to refer individual
fields.
DECLARE
TYPE tt_rectype IS RECORD (
referencekey tt.referencekey%TYPE,
spid tt.spid%TYPE,
nnsp tt.hiredate%TYPE,
onsp tt.deptno%TYPE,
portingtn tt.portingtn%TYPE);
TYPE tt_aa_type
IS TABLE OF TT_RECTYPE INDEX BY PLS_INTEGER;
tt_aa TT_AA_TYPE;
CURSOR cur_data IS
SELECT *
FROM tt
WHERE ROWID IN (SELECT ROWID
FROM (SELECT ROWID,
Row_number () over (PARTITION BY portingtn
,
nnsp
, onsp,
spid,
Trunc(
createddate
, 'MI') ORDER BY portingtn) dup
FROM tt)
WHERE dup > 1);
BEGIN
OPEN cur_data;
LOOP
FETCH cur_data BULK COLLECT INTO tt_aa LIMIT 1000;
FORALL i IN 1..tt_aa.COUNT
INSERT INTO soa_temp_sv_refkey_fordelete
(referencekey,
spid,
nnsp,
onsp,
portingtn)
(SELECT referencekey,
spid,
nnsp,
onsp,
portingtn
FROM tt
WHERE portingtn = Tt_aa(i).portingtn
AND spid = Tt_aa(i).spid
AND nnsp = Tt_aa(i).nnsp
AND onsp = Tt_aa(i).onsp
AND svid IS NULL);
EXIT WHEN cur_data%notfound;
END LOOP;
CLOSE cur_data;
COMMIT;
END;
*note this limitation is no longer present in Oracle 11g+
In addition, as #jonearles comments, you could just use a single SQL statement....
INSERT INTO soa_temp_sv_refkey_fordelete
(referencekey,
spid,
nnsp,
onsp,
portingtn)
SELECT referencekey,
spid,
nnsp,
onsp,
portingtn
FROM tt
WHERE ROWID IN (SELECT ROWID
FROM (SELECT ROWID,
Row_number () over (PARTITION BY portingtn, nnsp
, onsp,
spid,
Trunc(
createddate
, 'MI') ORDER BY portingtn) dup
FROM tt)
WHERE dup > 1);

Resources