ELSE Statement Not Printing - sql

I have a stored procedure that searches if a customer ID exists in a database. The procedure has two parameters, the customer ID and a number to indicated 1 if it was found and 2 if it is not. I am using and ELSE statement to print to screen is the ID is found or not. The valid ID I enter prints a valid message, but the invalid (non-existing ID) I enter does not. My ELSE put_line in my else block is not being executed(printed). Here is the procedure and the two function calls I make to it using a valid ID and an invalid ID. Why won't the put_line in the ELSE block print?
CREATE OR REPLACE PROCEDURE find_customer(cus_customer_id IN NUMBER, found OUT NUMBER) IS
cus_id NUMBER;
BEGIN
SELECT customer_id INTO cus_id FROM customers WHERE customer_id = cus_customer_id;
IF (cus_id != cus_customer_id)
THEN
found:=0;
dbms_output.Put_line('This Customer ID does not exist!');
ELSE
found:=1;
dbms_output.Put_line('This Customer ID (' || cus_id ||'} was found!');
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
found:=0;
WHEN OTHERS THEN
dbms_output.Put_line('error');
END;
-- FUNCTION CALL -- Find Customer Existing 319--
DECLARE
fnd NUMBER;
id NUMBER:= 319;
BEGIN
find_customer(id, fnd);
dbms_output.Put_line(fnd);
END;
-- FUNCTION CALL -- Find Customer Non-Existing 320--
DECLARE
fnd NUMBER;
id NUMBER:= 320;
BEGIN
find_customer(id, fnd);
dbms_output.Put_line(fnd);
END;
This is the output I am currently getting:
Procedure FIND_CUSTOMER compiled
This Customer ID (319) was found!
1
PL/SQL procedure successfully completed.
0
PL/SQL procedure successfully completed.

You are trying to find a row that does not exist and a NO_DATA_FOUND exception is thrown and then execution of the main block terminates and switches to executing the EXCEPTION block so the IF statement is never reached.
What you appear to want is:
CREATE PROCEDURE find_customer(
cus_customer_id IN CUSTOMERS.CUSTOMER_ID%TYPE,
found OUT NUMBER
) IS
cus_id NUMBER;
BEGIN
SELECT customer_id
INTO cus_id
FROM customers
WHERE customer_id = cus_customer_id;
found:=1;
dbms_output.Put_line('This Customer ID (' || cus_id ||'} was found!');
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.Put_line('This Customer ID does not exist!');
found:=0;
END;
/
Note: catching OTHERS in the exception handler is considered bad practice as it will hide all errors and you will not know when or why they happen. What you want to do is catch the expected exceptions and then, unless you have a very good reason not to, let the other exceptions cause a failure that you can then debug and fix.
db<>fiddle here

Related

if-else condition not working correctly in PL/SQL

I am writing a procedure to delete department from a table. It takes depatment id as argument and delete the department with given id. but it is not working correctly.When i didnot use EXCEPTION, it only give output when gien department id is present in table but if the id is not present in table it throw error. When i use exception, It did not check the if else condition.
Here is my procedure
CREATE OR REPLACE PROCEDURE del_job(j_id number) IS
jj_id bb_department.iddepartment%type;
BEGIN
SELECT IDDEPARTMENT
INTO jj_id
FROM BB_DEPARTMENT
WHERE IDDEPARTMENT=j_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
jj_id := NULL;
IF (jj_id=j_id) THEN
DELETE FROM BB_DEPARTMENT
WHERE IDDEPARTMENT=j_id;
dbms_output.put_line ('Job Deleted');
ELSIF(jj_id=0) THEN
dbms_output.put_line ('No Job Deleted');
END IF;
END;
/
Your indentation seems to imply that you want your if statement to be part of the normal flow rather than part of the exception block. But your actual code has the if statement in the exception handler. Since you're assigning a null to jj_id in the exception handler before running the if statement and null is never equal to nor unequal to any value, neither your if nor your elsif clause can ever be true so neither dbms_output call will be made.
Assuming your indentation shows your actual intent, my guess is that you want a nested PL/SQL block for the select statement and exception handler.
CREATE OR REPLACE PROCEDURE del_job(j_id number) IS
jj_id bb_department.iddepartment%type;
BEGIN
BEGIN
SELECT IDDEPARTMENT
INTO jj_id
FROM BB_DEPARTMENT
WHERE IDDEPARTMENT=j_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
jj_id := NULL;
END;
IF (jj_id=j_id) THEN
DELETE FROM BB_DEPARTMENT
WHERE IDDEPARTMENT=j_id;
dbms_output.put_line ('Job Deleted');
ELSIF(jj_id=0) THEN
dbms_output.put_line ('No Job Deleted');
END IF;
END;
/
You are making things overly difficult. DELETE sets sql%rowcount to the number of rows processed. So there is no need to select the department; just delete the appropriate id. If you want a confirmation message then test sql%rowcount. If the row was deleted it will contain 1 (or greater), if the id did not exist it will contain 0. Print the appropriate message.
create or replace procedure del_job(j_id number) is
begin
delete
from bb_department
where iddepartment=j_id;
if sql%rowcount > 0 then
dbms_output.put_line ('Job Deleted');
else
dbms_output.put_line ('No Job Deleted');
end if;
end del_job;
/

Procedure to check if authorized

So I have this function:
create or replace function get_authorization(
p_pnr in bankcustomer.pnr%type,
p_knr in account.cnr%type)
return number
as
v_authorization bankcustomer.pnr%type;
begin
select count(*)
into v_authorization
from bankcustomer,account
where pnr = p_pnr
and cnr = p_cnr;
return v_authorization;
exception
when no_data_found then
return -1;
end;
This returns 1 or 0 if allowed. I need to do a procedure which adds a row in a table called withdraw and that procedure needs to call get_authorization to check if the customer is allowed. This is what i have so far:
create or replace procedure do_withdraw(
p_pnr in withdraw.pnr%type,
p_belopp in withdraw.belopp%type)
as
declare
authorization exception;
begin
insert into uttag(pnr,amount)
values((select pnr from account),p_amount); /*pnr is foreign key in withdraw*/
if user not in (select get_amount(p_pnr) from dual) then
raise authorization;
end if;
exception
when authorization then
raise_application_error(-20007,'Unauthorized!');
commit;
end;
I get alot of error messages and specifically on declare. I really cant wrap my head around this :(
You have few problems with your code -
create or replace procedure do_withdraw(
p_pnr in withdraw.pnr%type,
p_belopp in withdraw.belopp%type)
as
-- declare -- Declare keyword is not used in procedure
authorization exception;
begin
insert into uttag(pnr,amount)
values((select pnr from account), -- This might return multiple rows, So you have to add a where clause in this query.
p_amount); /*pnr is foreign key in withdraw*/
if user <> get_authorization(p_pnr) then -- You are checking current user with amount which should be corrected.
raise authorization;
end if;
commit; -- Commit should be last sattement of procedure
exception
when authorization then
raise_application_error(-20007,'Unauthorized!');
Rollback; -- In exception you should use Rollabck instead of Commit;
end;
So ive tried what you said and think i know what you mean:
create or replace procedure do_withdraw(
p_pnr in withdraw.pnr%type,
p_amount in withdraw.amount%type)
as
-- declare -- Declare keyword is not used in procedure
authorization exception;
begin
insert into withdraw(pnr,amount)
values((select pnr from account where pnr = p_pnr), -- This might return multiple rows, So you have to add a where clause in this query.
p_amount); /*pnr is foreign key in withdraw*/
if user not in (select get_authorization(p_pnr)) then -- You are checking current user with amount which should be corrected.
raise authorization;
end if;
commit; -- Commit should be last sattement of procedure
exception
when authorization then
raise_application_error(-20007,'Unauthorized!');
Rollback; -- In exception you should use Rollabck instead of Commit;
end;
Now i get error: Line/Col: 11/51 PLS-00103: Encountered the symbol ")" when expecting one of the following:. ( , * % & - + /

SQL How to handle an exception in user defined functions?

CREATE OR REPLACE Function fun_Find_Staff_Name( v_staffid IN NUMBER )
RETURN VARCHAR2
IS
staff_name VARCHAR2(60);
CURSOR c_staff IS
SELECT staff_firstName || ' ' || staff_lastName
into staff_name
FROM staff
WHERE staff_id = v_staffid;
BEGIN
OPEN c_staff;
FETCH c_staff INTO staff_name;
CLOSE c_staff;
RETURN staff_name;
END;
This function takes a staff_id and returns the corresponding staff name. I want to handle the exception if the user enters an ID that is invalid and display an error message? Where do I add the exception block?
SET serveroutput ON;
DECLARE
v_staff_ID NUMBER := &StaffID;
v_message VARCHAR2(100);
BEGIN
v_message := fun_Find_Staff_Name(v_staff_ID);
dbms_output.put_line(v_message);
END;
I used this anonyomus block to check if the function works properly.
In my opinion you don't need an explicit cursor, an embedded sql will do the trick:
CREATE OR REPLACE Function fun_Find_Staff_Name
( v_staffid IN NUMBER )
RETURN VARCHAR2
IS
staff_name VARCHAR2(60);
BEGIN
SELECT staff_firstName || ' ' || staff_lastName
INTO staff_name
FROM staff WHERE staff_id = v_staffid;
RETURN staff_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
--in case of an invalid id no data will be found
--you can treat your exception here
--example
DBMS_OUTPUT.PUT_LINE('No data found for this id: ' || v_staffid);
RAISE;
WHEN OTHERS THEN
RAISE;
END fun_Find_Staff_Name ;
And please remember that when you are handling exceptions don't forget to raise it; if not the database will not rollback the transaction in case of an error.
If it yells some errors at the compilation, I am sorry, but I wrote the code without compiling it (in a hurry from my mob).
Cheers and happy coding.

PL/SQL Error PLS-00103 - Encountered symbol xxxx when expecting yyyy

I know these errors are most often caused by typos; that's what I've been finding at least. If my problem is a typo, I cannot see it after ~30 minutes of looking, it's driving me crazy.
My question is: am I doing something fundamentally wrong, or can you see a typo?
PL/SQL:
CREATE OR REPLACE PROCEDURE Replenish_Stock(p_id VARCHAR2, n INT)
AS
no_such_id EXCEPTION;
CURSOR pc IS
SELECT Product_ID FROM Products;
BEGIN
IF p_id IN pc THEN
UPDATE Products
SET Stock_Level = Stock_Level + n
WHERE product_id = p_id;
ELSE
RAISE no_such_id;
END IF;
EXCEPTION
WHEN INVALID_NUMBER THEN
DBMS_OUTPUT.PUT_LINE('Bad integer input, ignoring procedure call.');
WHEN no_such_id THEN
DBMS_OUTPUT.PUT_LINE('Bad Product id, ignoring procedure call.');
END;
/
Error code:
Error(7,14): PLS-00103: Encountered the symbol "PC" when expecting one of the following: (
Thanks for any help.
Your usage of IN and CURSOR is not right. the below should work for you. You can just use SQL%ROWCOUNT to see if the update query impact any rows in the table.
CREATE OR REPLACE PROCEDURE Replenish_Stock(p_id VARCHAR2, n INT)
AS
no_such_id EXCEPTION;
Rows_Updated NUMBER;
BEGIN
UPDATE Products
SET Stock_Level = Stock_Level + n
WHERE product_id = p_id;
IF( SQL%ROWCOUNT = 0) THEN
RAISE no_such_id;
END IF;
EXCEPTION
WHEN INVALID_NUMBER THEN
DBMS_OUTPUT.PUT_LINE('Bad integer input, ignoring procedure call.');
WHEN no_such_id THEN
DBMS_OUTPUT.PUT_LINE('Bad Product id, ignoring procedure call.');
END;
/

Why is the exception NO_DATA_FOUND not being triggered?

So the problem i am having is that if i execute the following procedure and the cursor doesnt find the parameter being passed, it continues to execute the block (insert statement) but instead of throwing the NO_DATA_FOUND exception error it throws a parent/foreign key error.
CREATE OR REPLACE PACKAGE ASSIGNMENT3 IS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE);
END ASSIGNMENT3;
/
CREATE OR REPLACE PACKAGE BODY ASSIGNMENT3 AS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS
CURSOR ADCOST_CUR IS
SELECT ACTUALCOST
FROM ADVERTISEMENT
WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE;
V_TOTALCOST NUMBER;
BEGIN
V_TOTALCOST := 0;
FOR INVOICE_REC IN ADCOST_CUR
LOOP
V_TOTALCOST := V_TOTALCOST + INVOICE_REC.ACTUALCOST;
END LOOP;
INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS)
VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL);
EXCEPTION WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('ERROR:The campaign title you entered returned no record(s), please enter a valid campaign title.');
COMMIT;
END END_CAMPAIGN;
END ASSIGNMENT3;
/
SET SERVEROUTPUT ON
EXECUTE ASSIGNMENT3.END_CAMPAIGN('Panasonic 3D TV');
While the parent foreign key error is correct, i dont want the block to execeute if the cursor doesnt return a row. Why is this happening?
Also, in terms of placing the COMMIT, where exactly do i tell it to COMMIT? Before the exception or after?
This is for a uni assignment.
When you loop over a cursor like that, if the cursor finds no matching rows, the loop simply doesn't execute at all. A NO_DATA_FOUND exception would only be raised if you had a SELECT ... INTO ... statement inside the BEGIN/END block that did not return any rows.
Where you have the COMMIT placed now, it is part of the EXCEPTION block -- but your indentation implies that you want it to execute whether the exception occurred or not. In this case, I would just put the COMMIT immediately after the INSERT, since it only matters if the INSERT is successful.
"So is there no way to have the NODATAFOUND exception trigger when
using a cursor, if the CTITLE parameter isnt found in the table"
What you could do is test the value of V_TOTAL_COST. If it is zero raise an exception, like this:
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS
CURSOR ADCOST_CUR IS
SELECT ACTUALCOST
FROM ADVERTISEMENT
WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE;
V_TOTALCOST NUMBER;
BEGIN
V_TOTALCOST := 0;
FOR INVOICE_REC IN ADCOST_CUR
LOOP
V_TOTALCOST := V_TOTALCOST + INVOICE_REC.ACTUALCOST;
END LOOP;
if v_total_cost = 0 then
raise no_data_found;
end if;
INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS)
VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL);
COMMIT;
EXCEPTION WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('ERROR:The campaign title you entered returned no record(s), please enter a valid campaign title.');
END END_CAMPAIGN;
This assumes you have a business rule that ACTUAL_COST cannot be zero.
Alternatively, there is the clunkier workaround of incrementing a counter in the loop and testing whether it is zero after the loop.
As for where to place the commit I would say the answer is not inside the procedure. The client (sqlplus in this case) should determine if the transaction will commit or rollback as the call to end the campaign may be just a part of a wider process. Also assuming that a campaign can exist without any advertisements then I would have an explicit check that the campaign title is valid perhaps against the table of CAMPAIGN? as suggested below:
CREATE OR REPLACE PACKAGE ASSIGNMENT3 IS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE);
END ASSIGNMENT3;
/
CREATE OR REPLACE PACKAGE BODY ASSIGNMENT3 AS
PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS
V_VALID_CAMPAIGN INTEGER;
V_TOTALCOST NUMBER;
BEGIN
-- Check this campaign title is valid
/* Will get you NO_DATA_FOUND here if CTITLE is invalid so wrap in
another BEGIN END block to throw own custom error that the client
of this procedure can handle (if it wants) */
BEGIN
SELECT 1
INTO V_VALID_CAMPAIGN
FROM CAMPAIGN
WHERE CAMPAIGNTITLE = CTITLE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20000,'The campaign title you entered returned no record(s), please enter a valid campaign title.');
END;
-- Now tot up the cost of ads in this campaign and raise the invoice
SELECT SUM(ACTUALCOST)
INTO V_TOTALCOST
FROM ADVERTISEMENT
WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE;
INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS)
VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL);
END END_CAMPAIGN;
END ASSIGNMENT3;
/
EXECUTE ASSIGNMENT3.END_CAMPAIGN('Panasonic 3D TV');
COMMIT;