NO DATA FOUND oracle using cursor - oracle

I have been searching online using different solution suggestion to handle no data in this code but to no avail. how can I handle the exception if no data is found. How can I solve this problem. I am not an expert in oracle though!
DECLARE
nCheckOption INT;
no_data_found EXCEPTION;
CURSOR TYPE_cursor IS
SELECT
D_NAL_REF.TRANS
, D_NAL_REF.INJ
, D_NAL_REF.REF
FROM D_NAL_REF D_NAL_REF
WHERE D_NAL_REF.REF IN
(SELECT AG_REF.REF
FROM AG_REF A_REF
WHERE A_REF.DESCEND_REF = 10
);
BEGIN
FOR rec IN TYPE_cursor
LOOP
nCheckOption := 0;
SELECT 1
INTO nCheckOption
FROM PERSON_TYPE WHERE TRANS = rec.TRANS AND INJ = rec.INJ;
IF nCheckOption = 1 THEN
UPDATE PERSON_TYPE
SET PERSON_TYPE.TYPE = rec.REF
WHERE TRANS = rec.TRANS
AND PERSON_TYPE.INJ = rec.INJ;
END IF;
EXCEPTION
WHEN no_data_found
THEN
DBMS_OUTPUT.PUT_LINE ('Trapped the error!?');
END LOOP;
END;
/

Rewrite your code to eliminate the inner SELECT, which is the only place in your code where I can see that a NO_DATA_FOUND exception could possibly be raised:
BEGIN
FOR rec IN (SELECT d.TRANS,
d.INJ,
d.REF
FROM D_NAL_REF d
WHERE d.REF IN (SELECT a.REF
FROM AG_REF a
WHERE a.DESCEND_REF = 10) AND
(d.TRANS, d.INJ) IN (SELECT DISTINCT TRANS, INJ
FROM PERSON_TYPE))
LOOP
UPDATE PERSON_TYPE
SET TYPE = rec.REF
WHERE TRANS = rec.TRANS AND
INJ = rec.INJ;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Trapped the error!?');
END;

I think you need to find if cursor contains any record or not. If cursor is empty then it must return that error message written in exception block.
Or you want to print error message if update statement do not find any record to update.
Here is the pseudo code.
Declare
ncheckoption number := 0;
Cursor type_cursor is ....
Begin
For rec in type_cursor loop
ncheckoption := ncheckoption + 1;
Update ...
If sql%rowcount = 0 then
Dbms_output.put_line('error message, if you want here in case no record found in table to update');
-- loop will continue
-- if you want loop to break then issue exit statement here
End if;
End loop;
If ncheckoption = 0 then
Dbms_output.put_line('error message you want to print in case cursor is empty');
End if;
End;
/
Cheers!!

Related

Unable to get dbms_sql.column_value

I'm trying to read a query from a table and process it with DBMS_SQL package. It parses correctly. I can also see the columns, which occurs in a query. But I'm not able to fetch results. In debug mode It throws the exception while trying to getting dbms_sql.column_value (please see the code below).
/*--------------------------------------------------------------------------------------------------------------------*/
PROCEDURE MY_PROC( nCSV_EXP_CFG_ID IN NUMBER,nN1 IN NUMBER DEFAULT null )
/*--------------------------------------------------------------------------------------------------------------------*/
as
l_ntt_desc_tab dbms_sql.desc_tab;
nCursorId INTEGER;
nColCnt INTEGER;
nRowCnt INTEGER;
rCSV_EXP_CFG CSV_EXP_CFG%ROWTYPE;
TYPE rowArray IS VARRAY(20) OF VARCHAR2(255);
colVal rowArray;
BEGIN
SELECT * INTO rCSV_EXP_CFG
FROM CSV_EXP_CFG
WHERE
CSV_EXP_CFG_ID = nCSV_EXP_CFG_ID;
nCursorId:=dbms_sql.open_cursor;
dbms_sql.parse(nCursorId, rCSV_EXP_CFG.EXPORT_TABLE, dbms_sql.native);
IF nN1 IS NOT NULL THEN DBMS_SQL.BIND_VARIABLE (nCursorId, 'n1', nN1); END IF;
dbms_sql.describe_columns(nCursorId, nColCnt, l_ntt_desc_tab);
FOR i IN 1..nColCnt LOOP
DBMS_OUTPUT.PUT_LINE( l_ntt_desc_tab(i).col_name);
--DBMS_SQL.DEFINE_COLUMN(nCursorId, i, colVal(i), 255);
END LOOP;
nRowCnt:=dbms_sql.execute(nCursorId);
LOOP
EXIT WHEN dbms_sql.fetch_rows(nCursorId) = 0;
FOR i IN 1..nColCnt LOOP
--here I'm getting the exception
dbms_sql.column_value(nCursorId, i, colVal(i));
END LOOP;
END LOOP;
Dbms_sql.close_cursor(nCursorId);
EXCEPTION WHEN OTHERS THEN
NULL;
END MY_PROC;
What am I doing wrong?

Subquery In if Statement (plsql)

I want to find if p_param2 is in subquery then do some operations according to this result. This subquery returns more than 1 row.
if p_param1 = 1
and p_param2 in (select code from x_table where code is not null) then
--..Some operations..
end if;
But I get PLS-00405 error. How can write this code effectively?
You need to get the needed value before IF..THEN statement within a seperate SQL query, while the current case is not possible. Try such a method which uses COUNT() aggregation without need of exception handling :
DECLARE
p_param1 ...
p_param2 ...
p_exists INT;
BEGIN
SELECT SIGN( COUNT(*) )
INTO p_exists
FROM x_table
WHERE code IS NOT NULL
AND code = p_param2;
IF p_param1 = 1 AND p_exists = 1 THEN
-- some operations
END IF;
END;
/
Declare a var v_dummy number. Then:
begin
select 1
into v_dummy
from x_table
where code = p_param2;
exception
when no_data_found then
v_dummy := 0;
end;
if p_param1 = 1 and v_dummy = 1 then
.
.
.
end if;
If x_table.code can contain duplicate values, you have to decide what to do when more than one occurrence of p_param2 is found.

Calling Cursor Value into Exception

I have a cursor as part of a package, which compares the counts of 2 tables. If the counts match, rest of the package executes. But if it fails, it should update the counts in a log table.
The cursor code is
CURSOR C_CNT IS
SELECT
lnd.sml_batchrun_id batch_id,
lnd.lnd_count,
dwh.dwh_count
FROM
(
SELECT
sml_batchrun_id,
COUNT(*) lnd_count
FROM
iva_landing.lnd_sml_t
GROUP BY
sml_batchrun_id
) lnd
LEFT JOIN (
SELECT
batchrun_id,
sent_records_count dwh_count,
dwh_sending_table
FROM
dwh.dwh_idh_to_iva_metadata_t
) dwh ON dwh.batchrun_id = lnd.sml_batchrun_id
WHERE dwh.dwh_sending_table = 'DWH_SML_T'
ORDER BY
1 DESC;
The comparison code is:
FOR L_COUNT IN C_CNT LOOP --0001,0002
IF L_COUNT.lnd_count = L_COUNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = L_COUNT.lnd_count,
idh.status = 'Verified'
WHERE L_COUNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_T';
COMMIT;
ELSE
RAISE EXCPT_SML_MISSDATA; -- Throw error and exit process
END IF;
END LOOP;
Now, in the Exception Handling part, I want to display the counts in the error column of the log table
logger.log_error('IVA-MSG 200-010 - COUNT MISMATCH! - Aborting', p_log_id=>l_job_log.log_id);
l_job_log.end_date := systimestamp;
l_job_log.error_mesg := cur_cnt.dwh_count||' '|| cur_cnt.lnd_count;
l_job_log.status := iva_log.iva_job_log_pck.c_str_statusfailed;
iva_log.iva_job_log_pck.change_joblog_prc(pi_rec_joblog => l_job_log);
RAISE;
Here, cur_cnt is a variable defined as cur_cnt c_cnt%rowtype;
and l_job_log as l_job_log iva_log.iva_job_log_t%rowtype;
where iva_job_log_t is the log table name.
But after triggering the package the count is not visible in the error column. Also, if I put something in single quotes for the iva_job_log_t then it gets displayed in the log table.
Please suggest how to display the counts from the cursor.
Thanks
If you are still doing
FOR L_COUNT IN C_CNT LOOP
then your cur_cnt variable is never going to be populated; it will be the empty record it was declared as, so referring to any of its fields will always give you null.
You can just refer to the l_count values in the error log:
l_job_log.error_mesg := l_count.dwh_count ||' '|| l_count.lnd_count;
... as you do elsewhere inside the loop.
If the exception handler is currently later in the process, so l_count is out of scope (as the re-raise possibly suggests) then you could move it so it's within the else instead of being separated:
FOR L_COUNT IN C_CNT LOOP --0001,0002
IF L_COUNT.lnd_count = L_COUNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = L_COUNT.lnd_count,
idh.status = 'Verified'
WHERE L_COUNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_T';
COMMIT;
ELSE
logger.log_error('IVA-MSG 200-010 - COUNT MISMATCH! - Aborting', p_log_id=>l_job_log.log_id);
l_job_log.end_date := systimestamp;
l_job_log.error_mesg := l_count.dwh_count||' '|| l_count.lnd_count;
l_job_log.status := iva_log.iva_job_log_pck.c_str_statusfailed;
iva_log.iva_job_log_pck.change_joblog_prc(pi_rec_joblog => l_job_log);
RAISE EXCPT_SML_MISSDATA; -- Throw error and exit process
END IF;
END LOOP;
Otherwise you would have to change the cursor loop handling to fetch into the variable, refer to that within the loop instead, and rely on that having the right values later:
OPEN C_CNT
LOOP
FETCH C_CNT INTO CUR_CNT;
EXIT WHEN C_CNT%NOTFOUND;
IF CUR_CNT.lnd_count = CUR_CNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = CUR_CNT.lnd_count,
idh.status = 'Verified'
WHERE CUR_CNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_T';
COMMIT;
ELSE
RAISE EXCPT_SML_MISSDATA; -- Throw error and exit process
END IF;
END LOOP;
Your later exception handler should then work as it is.

Check record exist before update

I want to update a table but I want to check if the record exist. If not then throw an exception. In the C# application I pass the parameters and execute the command by the following.
procedure usp_update_example
(
p_id in mydb.member.idn_member%type,
p_idn_person in mydb.member.idn_person%type,
p_ind_rep in mydb.member.ind_rep%type
)
as
v_exist pls_integer := 0;
v_step varchar2(250);
v_exception_not_exist exception;
begin
v_step := 'Check for record ' || p_id;
select count(1)
into v_exist
from mydb.member
where idn_member = p_id;
if v_exist = 0 then
raise v_exception_not_exist;
end if;
if (v_exist > 0) then
v_step := 'Update table :' || p_id;
update mydb.member
set
idn_person = p_idn_person,
ind_rep = p_ind_rep
where idn_member = p_id;
end if;
exception
when v_exception_not_exist then
Raise_application_error(-20001, 'Not exist');
end usp_update_example;
However even my condition is right, I do have the record existing in the table. I always get Not exist exception. If I don't use if v_exist = 0 and use WHEN NO_DATA_FOUND THEN. Then everything is fine.
I am not sure where is wrong.
Your code seems to be fine. Looks like this issue is related to some uncommitted data - you see the record in the session where you inserted it and you don't see it in C# session since the record is not committed yet. Hence, C# session generates exception.
What I would suggest re the procedure code is to make it more compact.
Something like the following:
...
begin
update mydb.member
set idn_person = p_idn_person,
ind_rep = p_ind_rep
where idn_member = p_id;
if SQL%ROWCOUNT = 0 then
raise_application_error(-20001,'Not exist');
end if;
end;

ora-01422 error by a procedure

Below code throwing ORA-01422 error. As my code uses select ... into I come to know it is fetching more than one row from the table but how can I overcome this by eliminating select into statement. Here is the code:
PROCEDURE Call_Transaction ( Transaction_Name Varchar2, Transaction_Type Varchar2, Form_Open_Type Varchar2 ) IS
BEGIN
Declare
M_Transaction_Name U_Transaction_Master.Transaction_Name%Type := Upper(Transaction_Name);
M_Transaction_Cd U_Transaction_Master.Transaction_Cd%Type;
T_Transaction_Cd U_Transaction_Master.Transaction_Cd%Type;
Begin
Select Transaction_Cd Into M_Transaction_Cd From U_Transaction_Master
Where Transaction_Name = M_Transaction_Name ;
Begin
Select Transaction_Cd Into T_Transaction_Cd From U_User_Wise_Transactions
Where Login_Cd = :Global.Login_Cd And Transaction_Cd = M_Transaction_Cd And
Inst_Cd = :Global.Company_Cd And
To_Char(Valid_Upto_Date,'DD-MM-YYYY') = '31-12-9999';
If Transaction_Type = 'FORM' And Upper(Form_Open_Type) = 'CALL_FORM' Then
DECLARE
id FormModule;
BEGIN
id := Find_Form(M_Transaction_Name); --<Replace your form name>--
IF Id_Null(id) THEN
Call_Form(:Global.Forms_Path||M_Transaction_Name||'.Fmx');
ELSE
Go_Form(Id) ;
END IF ;
END ;
Elsif Transaction_Type = 'FORM' And Upper(Form_Open_Type) = 'OPEN_FORM' Then
Open_Form(:Global.Forms_Path||M_Transaction_Name||'.Fmx');
Elsif Transaction_Type = 'REPORT' And Upper(Form_Open_Type) = 'RUN_PRODUCT' Then
Declare
Pl_Id ParamList;
Begin
Pl_Id := Get_Parameter_List('tmpdata');
IF NOT Id_Null(Pl_Id) THEN
Destroy_Parameter_List( Pl_Id );
END IF;
Pl_Id := Create_Parameter_List('tmpdata');
ADD_Parameter(pl_id,'Inst_Cd',TEXT_PARAMETER,:GLOBAL.Company_Cd);
ADD_Parameter(pl_id,'Ac_Year_Cd',TEXT_PARAMETER,:GLOBAL.Ac_Year_Cd);
ADD_Parameter(Pl_Id,'INST_NAME',TEXT_PARAMETER, :Global.Company_name);
ADD_Parameter(Pl_Id,'ADDRESS',TEXT_PARAMETER, :Global.Address);
ADD_Parameter(pl_id,'FOOTER',TEXT_PARAMETER,:GLOBAL.Footer);
Run_Product(REPORTS,:Global.Reports_Path||M_Transaction_Name, SYNCHRONOUS, RUNTIME,
FILESYSTEM, Pl_Id, NULL);
End;
End If;
Exception
When No_Data_Found Then
Message('Sorry..., You Do Not Have Authorization For : '||M_Transaction_Cd||' Transaction Code...');
Raise Form_Trigger_Failure;
End;
Exception
When No_Data_Found Then
Message('The Transaction Cd Not Exists In Transaction Master, Please Contact Administrator...');
Raise Form_Trigger_Failure;
End;
END;
How can I rewrite the code to resolve ORA-01422 error?
First, you need to change exception handling logic:
enclose in begin ... exception ... end only part that really can
through exception;
handle too_many_rows exception
.
PROCEDURE Call_Transaction ( Transaction_Name Varchar2, Transaction_Type Varchar2, Form_Open_Type Varchar2 ) IS
BEGIN
Declare
M_Transaction_Name U_Transaction_Master.Transaction_Name%Type := Upper(Transaction_Name);
M_Transaction_Cd U_Transaction_Master.Transaction_Cd%Type;
T_Transaction_Cd U_Transaction_Master.Transaction_Cd%Type;
Begin
-- 1st select with error analysis
begin
Select Transaction_Cd Into M_Transaction_Cd From U_Transaction_Master
Where Transaction_Name = M_Transaction_Name ;
exception
when No_Data_Found then begin
Message('The Transaction Cd Not Exists In Transaction Master, Please Contact Administrator...');
Raise Form_Trigger_Failure;
end;
when too_many_rows then begin
-- What really must be done in this case?
Message('There are too many Transaction Cd's with passed name In Transaction Master, Please Contact Administrator...');
Raise Form_Trigger_Failure;
end;
end;
-- 2nd select with error analysis
begin
Select Transaction_Cd Into T_Transaction_Cd From U_User_Wise_Transactions
Where Login_Cd = :Global.Login_Cd And Transaction_Cd = M_Transaction_Cd And
Inst_Cd = :Global.Company_Cd And
To_Char(Valid_Upto_Date,'DD-MM-YYYY') = '31-12-9999';
Exception
When No_Data_Found Then begin
Message('Sorry..., You Do Not Have Authorization For : '||M_Transaction_Cd||' Transaction Code...');
Raise Form_Trigger_Failure;
end;
When too_many_rows Then begin
-- What really must be done in this case?
Message('Sorry..., there are some misconfiguration in Authorization Settings For : '||M_Transaction_Cd||' Transaction Code. Please contact Administrator.');
Raise Form_Trigger_Failure;
end;
End;
If Transaction_Type = 'FORM' And Upper(Form_Open_Type) = 'CALL_FORM' Then
---[... all other code skipped ...]---
End If;
END;
After refactoring you need to answer a question about what really must be performed in situations when more than one row found and handle it according to nature of implemented task.
If you worried about method that you can use to detect presence of values and determine it's count then you may look at this question on StackOverflow.
In oracle, you can keep the select into statement and limit the number of rows using ROWNUM:
Select Transaction_Cd Into M_Transaction_Cd From U_Transaction_Master
Where Transaction_Name = M_Transaction_Name
and ROWNUM < 2;

Resources