How to check parameter value existence in a procedure? - sql

The following is a procedure that uses 2 parameters: customer_code and pay_amount.
The procedure works as expected, however when I enter wrong cus_code I get the error istead of my custom checking:
ORA-01403: no data found
ORA-06512: at "XXXXX.CUST_PAY", line 19
ORA-06512: at line 2
The procedure:
CREATE OR REPLACE PROCEDURE cust_pay (temp_ccode IN NUMBER, pay_amount IN NUMBER)AS
BEGIN
DECLARE
Value_check NUMBER;
cbalance NUMBER;
BEGIN
SELECT Count(cus_code)
INTO value_check
FROM customer
WHERE cus_code = temp_ccode;
IF value_check IS NULL THEN
Dbms_Output.put_line('the value was not FOUND');
ELSE
UPDATE customer
SET cus_balance = cus_balance - pay_amount
WHERE cus_code = temp_ccode;
SELECT cus_balance
INTO cbalance
FROM customer
WHERE cus_code = temp_ccode;
IF cbalance < 0 THEN
Dbms_Output.put_line('The client owes us ' || cbalance);
ELSE
Dbms_Output.put_line('Customer new balance is ' || cbalance);
END IF;
END IF;
END;
END;
What do I do wrong? I guest I need to check the value before I make SELECT request, right?

Your problem is actually caused by your check:
SELECT COUNT (cus_code)
INTO value_check
FROM customer
WHERE cus_code = temp_ccode;
The above query will never return NULL, which is what you are checking for. If no values in the table match the parameter temp_ccode then the value_check will be 0. This in turn means your IF statement is incorrect and this causes your error later in your code.
There's a simpler and more efficient way of doing this though. You can use SQL%ROWCOUNT to find out how many rows were effected by your UPDATE. If the return value is 0 then your customer doesn't exist.
create or replace procedure cust_pay (
Ptemp_ccode in number
, Ppay_amount in number
) is
l_balance number;
begin
update customer
set cus_balance = cus_balance - Ppay_amount
where cus_code = Ptemp_ccode;
if SQL%ROWCOUNT = 0 then
dbms_output.put_line('The customer was not found.');
else
select cus_balance
into l_balance
from customer
where cus_code = temp_ccode;
if l_balance < 0 then
dbms_output.put_line('the client owes us ' || l_balance);
else
dbms_output.put_line('customer new balance is ' || l_balance);
end if;
end if;
end;
There's no need to handle the NO_DATA_FOUND exception in the select... into ... here as you've already guaranteed that the cus_code exists by your UPDATE statement.
Please note the other changes I've made:
Different naming conventions for parameters and variables so it's clear in the code which is which.
Removal of the additional nested PL/SQL block, which was unnecessary.
Generally speaking you should never use dbms_ouput.put_line in a PL/SQL block as you have to be there to see what's happening. It's fine for debugging processes but is fairly useless in production code.
It's also possible to use the RETURN statement to avoid the nested IF statements, which I think makes the code cleaner and easier to read; though this is a judgement call.
create or replace procedure cust_pay (
Ptemp_ccode in number
, Ppay_amount in number
) is
l_balance number;
begin
update customer
set cus_balance = cus_balance - Ppay_amount
where cus_code = Ptemp_ccode;
if SQL%ROWCOUNT = 0 then
return;
end if;
select cus_balance
into l_balance
from customer
where cus_code = temp_ccode;
if l_balance < 0 then
dbms_output.put_line('The client owes us ' || l_balance);
else
dbms_output.put_line('New balance is ' || l_balance);
end if;
end;
All of this assumes that your CUSTOMER table is unique on cus_code.

Corrected version of your code is here:
CREATE OR REPLACE PROCEDURE cust_pay (temp_ccode IN NUMBER, pay_amount IN NUMBER) AS
Value_check NUMBER;
cbalance NUMBER;
BEGIN
SELECT Count(cus_code)
INTO value_check
FROM customer
WHERE cus_code = temp_ccode;
IF value_check = 0 THEN
Dbms_Output.put_line('the value was not FOUND');
ELSE
UPDATE customer
SET cus_balance = cus_balance - pay_amount
WHERE cus_code = temp_ccode
RETURNING cus_balance
INTO cbalance;
IF cbalance < 0 THEN
Dbms_Output.put_line('The client owes us ' || -cbalance);
ELSE
Dbms_Output.put_line('Customer new balance is ' || cbalance);
END IF;
END IF;
END;

Egor Skriptunoff has just posted an intriguing answer and I don't want to claim any credit for the idea so I'm posting this as a different answer. It's actually possible to do everything in a single statement.
Based on the principle advocated in my other answer that it doesn't matter if you update the balance of a non-existent customer you can combine my own and Egor's answers to come up with this:
create or replace procedure cust_pay (
Ptemp_ccode in number
, Ppay_amount in number
) is
l_balance number;
begin
update customer
set cus_balance = cus_balance - Ppay_amount
where cus_code = Ptemp_ccode
returning cus_balance
into l_balance;
if SQL%ROWCOUNT = 0 then
dbms_output.put_line('The customer was not found.');
elsif l_balance < 0 then
dbms_output.put_line('The client owes us ' || l_balance);
else
dbms_output.put_line('New balance is ' || l_balance);
end if;
end;
The benefit of doing everything in a single statement is an increase in speed. Once again you don't need to worry about the NO_DATA_FOUND exception as you're doing an update only.
Everything I said in my previous answer still holds true, count(*) in a select... into... will never return null, always a number.

You could have a check before the sql query to verify parameter values
Another approach is to use EXCEPTION NO_DATA_FOUND
CREATE OR REPLACE PROCEDURE cust_pay (temp_ccode IN NUMBER,
pay_amount IN NUMBER,errcode OUT NUMBER)
AS
BEGIN
DECLARE
Value_check NUMBER;
cbalance NUMBER;
BEGIN
SELECT COUNT (cus_code)
INTO value_check
FROM customer
WHERE cus_code = temp_ccode;
IF value_check IS NULL
THEN
DBMS_OUTPUT.put_line ('the value was not FOUND');
ELSE
UPDATE customer
SET cus_balance = cus_balance - pay_amount
WHERE cus_code = temp_ccode;
SELECT cus_balance
INTO cbalance
FROM customer
WHERE cus_code = temp_ccode;
IF cbalance < 0
THEN
DBMS_OUTPUT.put_line ('The client owes us ' || cbalance);
ELSE
DBMS_OUTPUT.put_line ('Customer new balance is ' || cbalance);
END IF;
END IF;
END;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('no data***' || SQLERRM);
errcode := 1;
END;

Related

it is showing error that ORA-00933: SQL command not properly ended, What to do?

select * from student_79;
DECLARE
total_rows number(2);
BEGIN
UPDATE student_10
SET age = age + 2;
IF sql%notfound THEN
dbms_output.put_line('no student updated');
ELSIF sql%found THEN
total_rows :=sql%rowcount;
dbms_output.put_line(total_rows || ' student updated ');
END IF;
END;
i check your code it is working goods no any error return.

Create a procedure to give bonuses to employees

I'm a beginner of PL/SQL.
I need to create a procedure.
The procedure name is sp_emp_bonus that takes 3 parameters:
department_id
job_id
amount
The procedure will update of bonus of employees within the department_id and job_id and increase existing bonus by the amount.
After the update statement, print using DBMS_OUTPUT.PUT_LINE to print out the number of employees affected by update (hint use implicit cursor attribute).
If no employees found then an exception section should capture NO_DATA_FOUND and print employees not found. Also include error handling for WHEN OTHERS ... and print DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM,1,100));
How to write this?
You can use simple update statement and sql%rowcount in procedure as follows:
CREATE OR REPLACE PROCEDURE SP_EMP_BONUS (
P_DEPARTMENT_ID IN NUMBER,
P_JOB_ID IN NUMBER,
P_AMOUNT IN NUMBER
) AS
LV_UPDATED_COUNT NUMBER := 0;
BEGIN
UPDATE YOUR_TABLE
SET
SALARY = SALARY + AMOUNT
WHERE DEPARTMENT_ID = P_DEPARTMENT_ID
AND JOB_ID = P_JOB_ID;
LV_UPDATED_COUNT := SQL%ROWCOUNT;
IF LV_UPDATED_COUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('no records found');
ELSE
DBMS_OUTPUT.PUT_LINE('Number of records updated: ' || LV_UPDATED_COUNT);
END IF;
END SP_EMP_BONUS;
/
Hope this will answer your question:
create or replace procedure sp_emp_bonus(
ip_dep_id in departments.department_id%type,
ip_job_id in jobs.job_id%type,
amount in number
)
is
no_emp_found exception;
pragma exception_init(no_emp_found, -20101);
cnt integer := 0;
begin
update
employees e
set e.salary = e.salary + nvl(amount, 0)
where
e.department_id = ip_dep_id and
e.job_id = ip_job_id;
cnt := sql%rowcount;
if cnt = 0 then
raise no_emp_found;
else
dbms_output.put_line('Employees updated ' || cnt);
end if;
commit;
exception
when no_emp_found then
rollback;
dbms_output.put_line('No Employees found for given Department and Job');
when OTHERS then
rollback;
DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM,1,100));
end;
Thanks.

Trigger compilation error

I need some help in creating a trigger.
create or replace trigger trigger_one
before insert on Funtom_timesheet
for each row
Declare
V_id number;
V_hours number;
Begin
Select max(timesheet_ID)+1 into v_id from Funtom_timesheet
:new.timesheet_ID :=v_id;
select grade_hours into V_hours
from funtom_grade join funtom_employee
on emp_grade = grade_id
where empid = :new.timesheet_emp;
if V_hours >:new.timesheet_hours
else
:new.timesheet_overtime :=
:new.timesheet_hours-V_hours
:new.timesheet_hours:= V_hours;
END IF;
END;
/
please tell me which part of my code is wrong so I could work on it,
Thanks
You have many syntax errors - missing ; and then. There can't be if only with else part and without expression on it.
CREATE OR REPLACE TRIGGER TRIGGER_ONE
BEFORE INSERT ON FUNTOM_TIMESHEET
FOR EACH ROW
DECLARE
V_ID NUMBER;
V_HOURS NUMBER;
BEGIN
SELECT MAX(TIMESHEET_ID) + 1 INTO V_ID FROM FUNTOM_TIMESHEET;
:NEW.TIMESHEET_ID := V_ID;
SELECT GRADE_HOURS
INTO V_HOURS
FROM FUNTOM_GRADE
JOIN FUNTOM_EMPLOYEE
ON EMP_GRADE = GRADE_ID
WHERE EMPID = :NEW.TIMESHEET_EMP;
IF V_HOURS > :NEW.TIMESHEET_HOURS THEN
NULL;
ELSE
:NEW.TIMESHEET_OVERTIME := :NEW.TIMESHEET_HOURS - V_HOURS;
:NEW.TIMESHEET_HOURS := V_HOURS;
END IF;
END;
/
Also better use SEQUENCES instead of:
SELECT MAX(TIMESHEET_ID) + 1 INTO V_ID FROM FUNTOM_TIMESHEET;
:NEW.TIMESHEET_ID := V_ID;
When selecting form the same table as inserting, you can get MUTATING trigger error (http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm)

Raise Application Error in Trigger and still update table

I'm facing some problem here, when the customer did not make payment in 60 days. The trigger will UPDATE the customer table and still RAISE the
error -20003.
What I've realize is that the update function will be canceled if the raise exception takes place.
Is there any way to go around doing this?
CREATE OR REPLACE TRIGGER cut_supply
BEFORE INSERT ON reading
FOR EACH ROW
DECLARE
cust_ID BILL.custID%TYPE;
sent_date BILL.sentDate%TYPE;
payment_date BILL.paymentdate%TYPE;
bad_Status CUSTOMER.badStatus%TYPE;
first_name CUSTOMER.firstName%TYPE;
last_name CUSTOMER.lastName%TYPE;
NRIC_No CUSTOMER.NRIC%TYPE;
pRate_ID CUSTOMER.pRateID%TYPE;
no_new_reading EXCEPTION;
CURSOR cust_cursor IS
SELECT b.custID, b.sentDate, b.paymentDate, c.badStatus, c.firstName, c.lastName, c.NRIC, c.pRateID
FROM bill b, customer c, reading r
WHERE (sysdate - sentDate) > 20
AND paymentDate is null
AND b.custID = c.custID
AND r.readingID = b.readingID
FOR UPDATE OF c.badStatus;
BEGIN
OPEN cust_cursor;
LOOP
FETCH cust_cursor INTO cust_ID, sent_date, payment_date,
bad_Status, first_name, last_name, NRIC_No, pRate_ID;
IF cust_cursor%NOTFOUND THEN
EXIT;
END IF;
IF cust_cursor%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE( 'No customer found');
ELSE
UPDATE customer
SET pRateID= 801, badStatus='Non Payment'
WHERE CURRENT OF cust_cursor;
RAISE no_new_reading;
END IF;
END LOOP;
CLOSE cust_cursor;
COMMIT;
EXCEPTION
WHEN no_new_reading THEN
RAISE_APPLICATION_ERROR ( -20003,
'Customer ' || first_name||''||last_Name ||
' water supply has been cut off' );
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/
this can be solve in the following manner, create a procedure to update the customer table
create or replace procedure update_customer
as
pragma autonomous_transaction;
begin
update customer c set c.badStatus='Non Payment', c.pRateID=801
where c.custID=
(
select distinct c.custID
FROM bill b, reading r
WHERE (sysdate - sentDate) > 20
AND paymentDate is null
AND b.custID = c.custID
AND r.readingID = b.readingID
);
commit;
end;
/
Use a trigger to call the procedure itself.
CREATE OR REPLACE TRIGGER cut_supply
BEFORE INSERT ON reading
FOR EACH ROW
DECLARE
cust_ID BILL.custID%TYPE;
sent_date BILL.sentDate%TYPE;
payment_date BILL.paymentdate%TYPE;
bad_Status CUSTOMER.badStatus%TYPE;
first_name CUSTOMER.firstName%TYPE;
last_name CUSTOMER.lastName%TYPE;
NRIC_No CUSTOMER.NRIC%TYPE;
reject_new_reading_excep EXCEPTION;
CURSOR cust_cursor IS
SELECT b.custID, b.sentDate, b.paymentDate, c.badStatus, c.firstName, c.lastName, c.NRIC
FROM bill b, customer c, reading r
WHERE (sysdate - sentDate) > 20
AND paymentDate is null
AND b.custID = c.custID
AND r.readingID = b.readingID;
BEGIN
OPEN cust_cursor;
LOOP
FETCH cust_cursor INTO cust_ID, sent_date, payment_date,
bad_Status, first_name, last_name, NRIC_No;
IF cust_cursor%NOTFOUND THEN
EXIT;
END IF;
IF cust_cursor%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE( 'No customer found');
ELSE
update_customer;
RAISE reject_new_reading_excep;
END IF;
END LOOP;
CLOSE cust_cursor;
COMMIT;
EXCEPTION
WHEN reject_new_reading_excep THEN
RAISE_APPLICATION_ERROR ( -20003,
'Customer ' || first_name||''||last_Name ||
' water supply has been cut off' );
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

Bind variable error in SQL trigger

Here is my trigger.Following error.
45 /
SP2-0552: Bind variable "OLD" not declared.
I am unable to find the error .I have tried several times but failed.I have declared every variable but the error is showing.Someone please help me .
drop trigger DELETE_Single_food
CREATE OR REPLACE TRIGGER DELETE_Single_food
BEFORE DELETE ON SingleFoodOrder
FOR EACH ROW
declare
f_price number;
old_bill number;
f_quantity number;
p_bill number;
foodID number;
orderID number;
order_date Date;
s_date Date;
f_date Date;
f_dis number;
a number;
b number;
c number;
BEGIN
IF :old.H_delivery_ID is not NULL then
foodID := :old.Fooditem_ID;
orderID := :old.H_delivery_ID;
f_quantity := :old.Quantity;
select bill into old_bill from HomeDelivery where H_delivery_ID =orderID ;
select price into f_price from Food_Item where Fooditem_ID = foodID;
select discount_percentage into f_dis from Food_Item where Fooditem_ID = foodID;
select start_date into s_date from Food_Item where Fooditem_ID = foodID;
select deadline into f_date from Food_Item where Fooditem_ID = foodID;
select H_delivery_request into order_date from HomeDelivery where H_delivery_ID =orderID;
if f_dis is not NULL then
if (order_date >= s_date and order_date<=f_date) then
a := f_dis/100;
b := f_price*a ;
c := f_price-b;
p_bill := old_bill-( c*f_quantity);
update HomeDelivery set bill = p_bill where H_delivery_ID = orderID;
else
p_bill := old_bill-(f_price*f_quantity);
update HomeDelivery set bill = p_bill where H_delivery_ID = orderID;
end if;
else
p_bill := old_bill-(f_price*f_quantity);
update HomeDelivery set bill = p_bill where H_delivery_ID = orderID;
end if;
END;
/
The problem is not in your variables declarations. I think you're not closing your main IF statement:
IF :old.H_delivery_ID is not NULL then
I don't see the END IF for that one. Add END IF; before the END;
If this does not work, you will need to do some debugging. Try just to leave only that statement in the begin block.
CREATE OR REPLACE TRIGGER DELETE_Single_food
BEFORE DELETE ON SingleFoodOrder
FOR EACH ROW
BEGIN
IF :old.H_delivery_ID is not NULL then
null;
END IF;
END;
/
See if that compiles. Then add the rest of the code to see where it breaks.
Hope this helps!