ORA-01403: no data found -- Exception handling not working - oracle

BEGIN
FOR v_LoadRec IN c_Load LOOP
SELECT count(1) INTO v_NO_OF_DAYS_RESP
from DIM_DATE
where DIM_DATE.TRADING_DAY_FLAG = 'Y' and
DIM_DATE_KEY <= TO_NUMBER(TO_CHAR(v_LoadRec.RESPONSE_DATE,'YYYYMMDD')) and
DIM_DATE_KEY >= TO_NUMBER(TO_CHAR(v_LoadRec.OPEN_DATE, 'YYYYMMDD'))
group by v_LoadRec.CALL_NUMBER;
IF SQL%NOTFOUND THEN
v_NO_OF_DAYS_RESP :='';
END IF;
SELECT count(1) INTO v_NO_OF_DAYS_RESO
from DIM_DATE
where DIM_DATE.TRADING_DAY_FLAG = 'Y' and
DIM_DATE_KEY <= TO_NUMBER(TO_CHAR(v_LoadRec.RESOLVE_DATE,'YYYYMMDD')) and
DIM_DATE_KEY >= TO_NUMBER(TO_CHAR(v_LoadRec.OPEN_DATE, 'YYYYMMDD'))
group by v_LoadRec.CALL_NUMBER;
IF SQL%NOTFOUND THEN
v_NO_OF_DAYS_RESO :='';
END IF;
END LOOP;
I have this block of SQL in my update procedure which gathers the count of trading days for each record and then inserts it into an integer variable named "v_NO_OF_DAYS_RESP" e.g. count of days between the open and response date of a call.
This works well except for when there is a null "RESPONSE_DATE" where it fails with the "ORA-01403: no data found" error. I understand why it's failing (because it of course has no record to insert) but I can't seem to figure out a way to get around it.
In these circumstances where the "RESPONSE_DATE" is found to be NULL, I would like the "v_NO_OF_DAYS_RESP" var to be set to NULL too (or even somehow have the SQL statement nested within an "IF" to possibly completely avoid running the calculation (SQL statement) when the "RESPONSE_DATE" is NULL).
*To put it really simple, I want the following:.. If the call does not yet have a response date, either don't run the SQL statement (calculation) or just set the var to Null
Any ideas or suggestions would be greatly appreciated.
Thanks - Kelvin

Handling exception will solve your problem:
BEGIN
FOR v_LoadRec IN c_Load LOOP
SELECT count(1) INTO v_NO_OF_DAYS_RESP
from DIM_DATE
where DIM_DATE.TRADING_DAY_FLAG = 'Y' and
DIM_DATE_KEY <= TO_NUMBER(TO_CHAR(v_LoadRec.RESPONSE_DATE,'YYYYMMDD')) and
DIM_DATE_KEY >= TO_NUMBER(TO_CHAR(v_LoadRec.OPEN_DATE, 'YYYYMMDD'))
group by v_LoadRec.CALL_NUMBER;
IF SQL%NOTFOUND THEN
v_NO_OF_DAYS_RESP :='';
END IF;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_NO_OF_DAYS_RESP :='';
END;

if (v_LoadRec.RESPONSE_DATE) is null Then
v_NO_OF_DAYS_RESP:='';
else
SELECT count(1) INTO v_NO_OF_DAYS_RESP
from DIM_DATE
where DIM_DATE.TRADING_DAY_FLAG = 'Y' and
DIM_DATE_KEY <= TO_NUMBER(TO_CHAR(v_LoadRec.RESPONSE_DATE, 'YYYYMMDD')) and
DIM_DATE_KEY >= TO_NUMBER(TO_CHAR(v_LoadRec.OPEN_DATE, 'YYYYMMDD'))
group by v_LoadRec.CALL_NUMBER;
IF SQL%NOTFOUND THEN
v_NO_OF_DAYS_RESP :='';
END IF;
end if;

Get the count of records in SELECT query. Then you can validate (count=0 or not).You can try like this.
result VARCHAR2(100);
result_count NUMBER;
BEGIN
SELECT count(<COLUMN_NAME>) INTO result_count FROM <TABLE_NAME> where empid = 12;
IF result_coun = 0 THEN
result := 'Value does not exist in the reference table';
END IF;
END;

Related

LOOP into cursor until each IF ELSE true in oracle

I have written a cursor where I want to LOOP each and every column until it becomes true. So if all the IF statement matches to true then I want to insert the data into VALID table or at last I want to insert the incorrect data into the INVALID TABLE.
Below is the cursor. Kindly let me know whether my step is accurate or Do I need to make any changes in that.
create or replace procedure fiber_transm_valid_data as
begin
for cur_r in (select rj_span_id,
rj_maintenance_zone_name,
rj_maintenance_zone_code
from app_fttx.transmedia#sat
)
loop
if cur_r.rj_span_id > '0' then
elsif cur_r.rj_maintenance_zone_name = 'aa' then
elsif cur_r.rj_maintenance_zone_code = 'A123' then
INSERT INTO VALID TABLE
(span_id, maintenance_zone_name,rj_maintenance_zone_code)
values (cur_r.rj_span_id, cur_r.rj_maintenance_zone_name, cur_r.rj_maintenance_zone_code);
ELSE
INSERT INTO INVALID TABLE
(span_id, maintenance_zone_name,rj_maintenance_zone_code)
values (cur_r.rj_span_id, cur_r.rj_maintenance_zone_name, cur_r.rj_maintenance_zone_code);
end loop;
end fiber_transm_valid_data;
Not quite like that; IF is wrong. Have a look at this.
create or replace procedure fiber_transm_valid_data as
l_state_name table_of_states.rj_state_name%type;
begin
for cur_r in (select rj_span_id,
rj_maintenance_zone_name,
rj_maintenance_zone_code,
rj_state_name
from app_fttx.transmedia#sat
)
loop
select max(rj_state_name)
into l_state_name
from table_of_states
where rj_state_name = cur_r.rj_state_name
and rownum = 1;
if cur_r.rj_span_id > '0'
and cur_r.rj_maintenance_zone_name = 'aa'
and cur_r.rj_maintenance_zone_code = 'A123'
and l_state_name = 1
then
INSERT INTO VALID_TABLE
(span_id, maintenance_zone_name,rj_maintenance_zone_code)
values
(cur_r.rj_span_id, cur_r.rj_maintenance_zone_name, cur_r.rj_maintenance_zone_code);
else
INSERT INTO INVALID_TABLE
(span_id, maintenance_zone_name,rj_maintenance_zone_code)
values
(cur_r.rj_span_id, cur_r.rj_maintenance_zone_name, cur_r.rj_maintenance_zone_code);
end if;
end loop;
end fiber_transm_valid_data;

ORA-01001: invalid cursor in a query using the 'for' loop

I am trying to write a simple query using cursors, however I keep getting this notorious 'invalid cursor' error. Here's my code.
DECLARE
v_deptno NUMBER := 10;
c_last_name employees.last_name%TYPE;
c_salary employees.salary%TYPE;
c_manager_id employees.salary%TYPE;
CURSOR c_emp_cursor IS
SELECT last_name, salary, manager_id FROM employees
WHERE department_id = v_deptno;
BEGIN
--OPEN c_emp_cursor;
-- commented out because for loop opens the cursor automatically
FOR employee IN c_emp_cursor
LOOP
FETCH c_emp_cursor INTO c_last_name, c_salary, c_manager_id;
EXIT WHEN c_emp_cursor%NOTFOUND;
IF c_salary < 5000 THEN
IF c_manager_id = 101 OR c_manager_id = 124 THEN
dbms_output.put_line(c_last_name || 'due for a raise.');
ELSE
dbms_output.put_line(c_last_name || ' not due for a raise.');
END IF;
END IF;
END LOOP;
--CLOSE c_emp_cursor;
END;
What could be wrong and what is the solution for this? I have already tried to apply possible solutions for similar questions, but none of them really match my problem.
As you commented out OPEN and CLOSE, you should remove FETCH and EXIT as well, for the same reason. Furthermore, now you have a cursor variable within the FOR statement, so - use it. Something like this:
BEGIN
FOR employee IN c_emp_cursor
LOOP
IF employee.salary < 5000 THEN
IF employee.manager_id = 101 OR employee.manager_id = 124 THEN
dbms_output.put_line(employee.last_name || 'due for a raise.');
ELSE
dbms_output.put_line(employee.last_name || ' not due for a raise.');
END IF;
END IF;
END LOOP;
END;

How to use CURSORS to find No. of rows affected by UPDATE query

CREATE TABLE cursor_example(
emp_id NUMBER(10) PRIMARY KEY,
emp_name VARCHAR2(30),
emp_salary NUMBER(6)
);
SELECT * FROM cursor_example;
INSERT INTO cursor_example VALUES(1234,'apple',1250);
INSERT INTO cursor_example VALUES(1235,'banana',1500);
INSERT INTO cursor_example VALUES(1236,'carrot',1750);
INSERT INTO cursor_example VALUES(1237,'donkey',2050);
INSERT INTO cursor_example VALUES(1238,'elixr',15075);
UPDATE cursor_example
SET emp_salary = emp_salary + (.25*emp_salary);
DECLARE affected_emp NUMBER(2);
BEGIN
UPDATE cursor_example
SET emp_salary = emp_salary + (.25*emp_salary);
IF sql%notfound THEN
DBMS_OUTPUT.PUTLINE('NO PEOPLE AFFECTED');
ELSEIF sql%found THEN
DBMS_OUTPUT.PUTLINE(affected_emp || 'PEOPLE AFFECTED');
ENDIF;
END;
The error message I got is :
ORA-06550: line 7, column 12: PLS-00103: Encountered the symbol "SQL"
when expecting one of the following: := . ( # % ;
Your actual error is caused by invalid syntax. In PL/SQL it's ELSIF not ELSEIF, although as you're not implementing a multi-branched switch you just need ELSE. Also END IF is two words. Furthermore it's dbms_output.put_line() not putline.
If you fix all those errors you routine will run.
However, sql%found tells us whether our DML hit any records but doesn't tell us how many records. So affected_emp will be null in your code.
The easiest way to find the number of records affected is sql%rowcount which gives us the number of records inserted, updated or deleted by the preceding DML statement. Zero means no records were changed.
Obviously you could just output that figure but here's how it looks in your code:
DECLARE
affected_emp NUMBER(2);
BEGIN
UPDATE cursor_example
SET emp_salary = emp_salary + (.25*emp_salary);
affected_emp := sql%rowcount;
IF affected_emp = 0 THEN
DBMS_OUTPUT.PUT_LINE('NO PEOPLE AFFECTED');
ELSE
DBMS_OUTPUT.PUT_LINE(affected_emp || ' PEOPLE AFFECTED');
END IF;
END;
You should use ELSIF (or ELSE) :
ELSIF sql%found THEN
DBMS_OUTPUT.put_line(affected_emp || 'PEOPLE AFFECTED');
END IF;
Or ELSE:
ELSE
DBMS_OUTPUT.put_line(affected_emp || 'PEOPLE AFFECTED');
END IF;
syntax for IF-THEN-ELSIF-ELSE in Oracle/PLSQL is:
IF condition1 THEN
{...statements to execute when condition1 is TRUE...}
ELSIF condition2 THEN
{...statements to execute when condition2 is TRUE...}
ELSE
{...statements to execute when both condition1 and condition2 are FALSE...}
END IF;

Handle null values in BULK COLLECT

I trying to pass data from table A to table B, but first I need to check if the data I'm trying to insert is not in the table B. I do this with a query if exists will return the identification. The problem is the second execution throws a unique constraint violation so it appears the validation to compare if exist in table B is not working, or the condition in the if statement is wrong.
DECLARE
LN_EXIST NUMBER;
CURSOR cur
IS
SELECT *
FROM table_A
TYPE cur_aat IS TABLE OF cur%ROWTYPE
INDEX BY PLS_INTEGER;
cur_rows cur_aat;
BEGIN
OPEN cur;
LOOP
FETCH cur BULK COLLECT INTO cur_rows LIMIT 1000;
EXIT WHEN cur%NOTFOUND; /* cause of missing rows */
FOR I IN 1 .. cur_rows.COUNT
LOOP
LN_EXIST := 0;
BEGIN
DBMS_OUTPUT.put_line (cur_rows (I).PEU_IDENTIFICACION);
SELECT PUV.PEU_IDENTIFICACION -- check
INTO LN_EXIST
FROM table_b PUV
WHERE (CASE
WHEN PUV.PEU_IDENTIFICACION =
cur_rows (I).PEU_IDENTIFICACION
AND NVL (PUV.PEU_PRIMER_NOMBRE, '0') =
NVL (cur_rows (I).PEU_PRIMER_NOMBRE, '0')
AND NVL (PUV.PEU_SEGUNDO_NOMBRE, '0') =
NVL (cur_rows (I).PEU_SEGUNDO_NOMBRE, '0')
AND PUV.PEU_PRIMER_APELLIDO =
cur_rows (I).PEU_PRIMER_APELLIDO
AND NVL (PUV.PEU_SEGUNDO_APELLIDO, '0') =
NVL (cur_rows (I).PEU_SEGUNDO_APELLIDO,
'0')
AND PUV.PEU_FECHA_NACIMIENTO =
cur_rows (I).PEU_FECHA_NACIMIENTO
THEN
'S'
ELSE
'N'
END) = 'S';
EXCEPTION
WHEN OTHERS
THEN
LN_EXIST:= 0;
END;
IF LN_EXIST!= 0 --if not exist
THEN
INSERT
INTO table_b (PEU_ID,
PEU_IDENTIFICACION,
PEU_PRIMER_APELLIDO,
PEU_SEGUNDO_APELLIDO,
PEU_PRIMER_NOMBRE,
PEU_SEGUNDO_NOMBRE,
)
VALUES (cur_rows (I).PEU_ID,
cur_rows (I).PEU_TIPO_IDENTIFICACION,
cur_rows (I).PEU_IDENTIFICACION,
cur_rows (I).PEU_PRIMER_APELLIDO,
cur_rows (I).PEU_SEGUNDO_APELLIDO,
cur_rows (I).PEU_PRIMER_NOMBRE,
cur_rows (I).PEU_SEGUNDO_NOMBRE,
);
END IF;
END LOOP;
EXIT WHEN cur%NOTFOUND;
END LOOP;
COMMIT;
CLOSE cur;
END;
First of all, the code shown above won't execute because of syntax errors in the lines which read
PEU_SEGUNDO_NOMBRE,
and
cur_rows (I).PEU_SEGUNDO_NOMBRE,
The trailing commas will cause the compilation to fail.
Second, row-by-row processing tends to be slow, even when you bulk collect the data into memory from the database. I suggest you use a MERGE, which replaces your cursor-and-loop logic with a single statement:
MERGE INTO TABLE_B b
USING TABLE_A a
ON (b.PEU_IDENTIFICACION = a.PEU_IDENTIFICACION AND
NVL(b.PEU_PRIMER_NOMBRE, '0') = NVL (a.PEU_PRIMER_NOMBRE, '0') AND
NVL (b.PEU_SEGUNDO_NOMBRE, '0') = NVL(a.PEU_SEGUNDO_NOMBRE, '0') AND
b.PEU_PRIMER_APELLIDO = a.PEU_PRIMER_APELLIDO AND
NVL(b.PEU_SEGUNDO_APELLIDO, '0') = NVL(a.PEU_SEGUNDO_APELLIDO, '0') AND
b.PEU_FECHA_NACIMIENTO = a.PEU_FECHA_NACIMIENTO)
WHEN NOT MATCHED THEN
INSERT (PEU_ID,
PEU_IDENTIFICACION,
PEU_PRIMER_APELLIDO,
PEU_SEGUNDO_APELLIDO,
PEU_PRIMER_NOMBRE,
PEU_SEGUNDO_NOMBRE)
VALUES (a.PEU_ID,
a.PEU_TIPO_IDENTIFICACION,
a.PEU_IDENTIFICACION,
a.PEU_PRIMER_APELLIDO,
a.PEU_SEGUNDO_APELLIDO,
a.PEU_PRIMER_NOMBRE,
a.PEU_SEGUNDO_NOMBRE);
Best of luck.
I checked, instead of use a select statement only, i added a count statement, to determine the total of coincidences that exist with the parameters i set so all the times will return a value if not exist 0 and if exist other value than 0.
DECLARE
LN_EXIST NUMBER;
CURSOR cur
IS
SELECT *
FROM table_A
TYPE cur_aat IS TABLE OF cur%ROWTYPE
INDEX BY PLS_INTEGER;
cur_rows cur_aat;
BEGIN
OPEN cur;
LOOP
FETCH cur BULK COLLECT INTO cur_rows LIMIT 1000;
EXIT WHEN cur%NOTFOUND; /* cause of missing rows */
FOR I IN 1 .. cur_rows.COUNT
LOOP
DBMS_OUTPUT.put_line (cur_rows (I).PEU_IDENTIFICACION);
SELECT COUNT (PUV.PEU_IDENTIFICACION) -- check
INTO LN_EXIST
FROM table_b PUV
WHERE (CASE
WHEN PUV.PEU_IDENTIFICACION =
cur_rows (I).PEU_IDENTIFICACION
AND NVL (PUV.PEU_PRIMER_NOMBRE, '0') =
NVL (cur_rows (I).PEU_PRIMER_NOMBRE, '0')
AND NVL (PUV.PEU_SEGUNDO_NOMBRE, '0') =
NVL (cur_rows (I).PEU_SEGUNDO_NOMBRE, '0')
AND PUV.PEU_PRIMER_APELLIDO =
cur_rows (I).PEU_PRIMER_APELLIDO
AND NVL (PUV.PEU_SEGUNDO_APELLIDO, '0') =
NVL (cur_rows (I).PEU_SEGUNDO_APELLIDO,
'0')
AND PUV.PEU_FECHA_NACIMIENTO =
cur_rows (I).PEU_FECHA_NACIMIENTO
THEN
'S'
ELSE
'N'
END) = 'S';
IF LN_EXIST!= 0 --if not exist
THEN
INSERT
INTO table_b (PEU_ID,
PEU_IDENTIFICACION,
PEU_PRIMER_APELLIDO,
PEU_SEGUNDO_APELLIDO,
PEU_PRIMER_NOMBRE,
PEU_SEGUNDO_NOMBRE,
)
VALUES (cur_rows (I).PEU_ID,
cur_rows (I).PEU_TIPO_IDENTIFICACION,
cur_rows (I).PEU_IDENTIFICACION,
cur_rows (I).PEU_PRIMER_APELLIDO,
cur_rows (I).PEU_SEGUNDO_APELLIDO,
cur_rows (I).PEU_PRIMER_NOMBRE,
cur_rows (I).PEU_SEGUNDO_NOMBRE,
);
END IF;
END LOOP;
EXIT WHEN cur%NOTFOUND;
END LOOP;
COMMIT;
CLOSE cur;
END;

How Can I code a "IF UPDATING" trigger in Oracle Database 10g?

I'm coding a Trigger to ensure only one type of money can be set as official. My intention is code a "BEFORE INSERT OR UPDATE" trigger. The INSERT section works fine but the problem is coding the UPDATING section because when I try to update the table I recieve ORA-04091 "mutanting table". Do you have any idea?
Table (Only one record can be set as 'Y'):
mon_id mon_description mon_official
----------------------------------------------
E EUR N
D DOL N
P PES Y
Trigger:
CREATE OR REPLACE TRIGGER mon_oficial_ins_trg
BEFORE
INSERT OR UPDATE
ON monedas
FOR EACH ROW
DECLARE
v_count NUMBER(8);
BEGIN
IF INSERTING THEN
SELECT COUNT(mon_oficial)
INTO v_count
FROM monedas
WHERE mon_oficial = 'Y';
IF v_count = 1 THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
IF UPDATING THEN
SELECT COUNT(:OLD.mon_oficial)
INTO v_count
FROM monedas
WHERE :OLD.mon_oficial = 'Y';
IF v_count = 1 AND :NEW.mon_oficial = 'Y' THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
END mon_oficial_ins_trg;
/
SHOW ERRORS;
In your code there are 2 mistake
first
SELECT COUNT(:OLD.mon_oficial)
INTO v_count
FROM monedas
WHERE :OLD.mon_oficial = 'Y';
part, for more information about mutanting error you can read this article
enter link description here
and second mistake, you have a incorrect logic in
IF v_count = 1 AND :NEW.mon_oficial = 'Y' THEN part because it can be our current row
try it
CREATE OR REPLACE TRIGGER mon_oficial_ins_trg
BEFORE
INSERT OR UPDATE
ON monedas
FOR EACH ROW
DECLARE
v_count NUMBER(8);
BEGIN
IF INSERTING THEN
SELECT COUNT(mon_oficial)
INTO v_count
FROM monedas
WHERE mon_oficial = 'Y';
IF v_count = 1 THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
IF UPDATING THEN
IF :NEW.mon_oficial = 'Y' then
for m in (SELECT *
FROM monedas
WHERE mon_oficial = 'Y'
and rownum=1) loop
IF :NEW.mon_id <> m.mon_id THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
end loop;
END IF;
END mon_oficial_ins_trg;
/
SHOW ERRORS;
This can be done quite simply with an AFTER INSERT OR UPDATE statement trigger. The same logic is applicable to both operations.
SELECT COUNT(*) INTO v_count FROM MONEDAS
WHERE MON_OFICIAL = 'Y';
IF v_count > 1 THEN
RAISE_APPLICATION_ERROR...
Another advantage of this approach: it allows the statement
UPDATE MONEDAS SET MON_OFICIAL = CASE MON_ID WHEN 'A' THEN 'Y' ELSE 'N' END;
to work without a problem when the row-level trigger might raise an error if the row with MON_ID = 'A' is updated to Y before the previous official currency is updated to N.
I think this problem would better be solved with a constraint, instead of a trigger. I am not the author of this answer, but I think it's relevant here. In the rest of the answers there was a link to a blog post that recommends avoiding triggers, but that link doesn't seem to work.
The answer is found here:
https://stackoverflow.com/a/182427
Here's the example provided in that answer by #tony-andrews:
create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);

Resources