Having trouble creating trigger that updates column based on variable - sql

I'm trying to create a trigger for a class that updates customer balance depending on how many days late or early the item was returned. I have a charter table that has a due date and return date, the trigger is designed to only fire when the return date is being updated. The trigger then takes the difference between return date and due date and stores that value into a variable. I have a series of if else statements that determine whether the item was returned late or early and then multiply the number of days by the late fee or early bonus. Then it updates the customer balance in the customer table to whatever the value of the fee variable is. Oracle is saying I have a syntax error on my end statement and I'm not sure what is wrong.
CREATE OR REPLACE TRIGGER Fee_Trigger
AFTER UPDATE ON CHARTER
FOR EACH ROW
WHEN ((:NEW.Return_Date <> :OLD.Return_Date AND :NEW.Return_Date IS NOT
NULL))
DECLARE
Fee NUMBER;
BEGIN
Fee := (:NEW.Return_Date - Due_Date);
IF Fee > 0 THEN Fee := (Fee * 75) ;
ELSE IF Fee < 0 THEN Fee := (Fee * 25);
ELSE IF Fee = 0 THEN FEE := Fee;
END IF;
UPDATE CUSTOMER
SET Customer_Balance = Fee
WHERE CustomerID = :NEW.CustomerID
END;

There are some little formatting errors. The following may be used, alternatively :
CREATE OR REPLACE TRIGGER Fee_Trigger
AFTER UPDATE ON CHARTER
FOR EACH ROW
WHEN ((NEW.Return_Date <> OLD.Return_Date AND NEW.Return_Date IS NOT NULL))
DECLARE
Fee NUMBER;
BEGIN
Fee := :NEW.Return_Date - :NEW.Due_Date;
IF Fee > 0 THEN Fee := (Fee * 75);
ELSIF Fee < 0 THEN Fee := (Fee * 25);
ELSIF Fee = 0 THEN Fee := Fee;
END IF;
UPDATE CUSTOMER
SET Customer_Balance = Fee
WHERE CustomerID = :NEW.CustomerID;
END;
The following issues encountered :
Due_date was not defined (:OLD.Due_Date or :NEW.Due_Date may be used)
ELSIF should be used instead of ELSE IF
UPDATE statement should be ended with a semicolon.
Remove colons before OLD and NEW inside WHEN statement.

I suggest replacing the IF/ELSIF statements with a CASE expression in the UPDATE statement:
CREATE OR REPLACE TRIGGER Fee_Trigger
AFTER UPDATE ON CHARTER
FOR EACH ROW
WHEN ((:NEW.Return_Date <> :OLD.Return_Date AND :NEW.Return_Date IS NOT NULL))
DECLARE
Due_Date DATE := TRUNC(SYSDATE); -- initialization as an example value.
nDays_between_return_and_due NUMBER;
BEGIN
nDays_between_return_and_due := TRUNC(:NEW.Return_Date - Due_Date);
UPDATE CUSTOMER
SET Customer_Balance = CASE
WHEN nDays_between_return_and_due > 0 THEN -- Returned late
nDays_between_return_and_due * 75
WHEN nDays_between_return_and_due < 0 THEN -- Returned early
nDays_between_return_and_due * 25
ELSE -- Returned on time
0
END
WHERE CustomerID = :NEW.CustomerID;
END FEE_TRIGGER;
Best of luck.

Related

adding to variables inside of a oracle procedure

I created a procedure that takes the account number of a user and it returns every transaction they've made plus their running balance. everything works except the balance. The code compiles and runs but nothing shows up in the running balance column of the dbms.output
CREATE OR REPLACE PROCEDURE print_dett(
in_account_nbr NUMBER
)
IS
dCount NUMBER;
BEGIN
dbms_output.put_line(' Date : transaction amount : balance - For: ' || in_account_nbr);
FOR r IN(
SELECT t.tx_nbr, t.tx_date, t.tx_amount, t.tx_type_code, a.balance
FROM transaction t, account a
WHERE t.account_nbr = a.account_nbr
AND t.account_nbr = in_account_nbr
ORDER BY tx_date)
LOOP
IF r.tx_type_code = 'D' OR r.tx_type_code = 'R' THEN
dCount := dCount + r.tx_amount;
ELSE
dCount := dCount - r.tx_amount;
END IF;
dbms_output.put_line(r.tx_date || ' : ' || r.tx_amount || ' : ' || dCount );
END LOOP;
END;
What can I change in the code so the running balance will actually show up when printed?
Number variables get initialized with NULL. Your variable dCount is hence null first and adding values doesn't change this.
Initialize it with zero instead:
CREATE OR REPLACE PROCEDURE print_dett(
in_account_nbr NUMBER
)
IS
dCount NUMBER := 0;
...

IF THEN STATEMENT ORACLE APEX PS/SQL

I am trying to calculate taxes, however, I keep getting an error ORA-06550. Can you please aid me?
How it should to work is
if gross pay is more than 225000 then we divide the gross pay by 3.
if gross pay is less than 225000 then set the value at 75000
This is what I tried so far:
IF nvl(:P37_GROSS_PAY,0) > (225000*nvl:(P37_PERIOD,0)) THEN
{nvl(:P37_GROSS_PAY,0)/3}
ELSE
{75000*nvl(:P37_PERIOD,0)}
END IF;
You have to put the result into something, such as a locally declared variable (and remove curly brackets; they are invalid in this context):
declare
tax number;
begin
if nvl(:P37_GROSS_PAY, 0) > 22500 * nvl(:P37_PERIOD, 0)) then
tax := nvl(:P37_GROSS_PAY, 0) / 3;
else
tax := 75000 * nvl(:P37_PERIOD, 0);
end if;
end;

Looking at IF, WHERE, and WHEN in PL SQL

I am completing an academic assignment that asks to prompt the user for their target sales and their employee id. If the target sales exceeds or equates to that of the actual company sales for 2015, then raises can be applied.
I've composed a majority of code but I am stuck on the END IF; statements on line 25. I'm receiving error(s)
ORA-06550, PLS-00103: Encountered the symbol "WHERE" when expecting
one of the following.
I think I might be struggling to integrate the if statement that compares the user input to the company sales for 2015.
Insights greatly appreciated! Thank you!
accept emp_target prompt 'Please enter your company sales target: '
accept empno prompt 'Please enter your employee ID: '
DECLARE
emp_target NUMBER := &emp_target;
cmp_target NUMBER;
empno emp_employees.emp_id%type := &empno;
new_sal emp_employees.salary%type;
cnt number;
CURSOR sales_cur IS
SELECT SUM(oe_orderDetails.quoted_price)
FROM oe_orderDetails
JOIN oe_orderHeaders
ON oe_orderDetails.order_id = oe_orderHeaders.order_id
WHERE oe_orderHeaders.order_date >= to_date('1.1.' || 2015, 'DD.MM.YYYY')
and oe_orderHeaders.order_date < to_date('1.1.' || (2015 + 1), 'DD.MM.YYYY');
BEGIN
OPEN sales_cur;
FETCH sales_cur INTO cmp_target;
IF cmp_target >= emp_target THEN
UPDATE emp_employees SET
emp_employees.salary = case WHEN emp_employees.dept_id = 10 THEN emp_employees.salary * 1.1
WHEN emp_employees.emp_id = 145 THEN emp_employees.salary * 1.15
WHEN emp_employees.dept_id = 80 THEN emp_employees.salary * 1.2
ELSE emp_employees.salary
END IF;
END
WHERE emp_employees.emp_id = empno
returning emp_employees.salary into new_sal;
cnt := sql%rowcount;
IF cnt > 0 THEN
dbms_output.put_line('Employee ' || empno || ', new salary = ' || new_sal);
ELSE
dbms_output.put_line('Nobody got new salary');
END IF;
END;
/
The main issue is that you have misplaced the CASE block's end and where clause out after END IF.
Apart from that I would say that the cursor block was not required to store a simple SUM, but i'll let you use it since you are learning for an assignment.
The other problem after you fix the first one is your returning emp_employees.salary into new_sal. A scalar variable can't contain multiple rows returned from the dml which updates more than one row. You should use a collection(nested table) instead. Use RETURNING BULK COLLECT INTO to load it and loop over later to display your final message.
Please read all my code comments carefully. I cannot test the whole code for any errors as I have none of your tables. You should fix if there are any if you can or let us know if you can't.
ACCEPT emp_target PROMPT 'Please enter your company sales target: '
ACCEPT empno PROMPT 'Please enter your employee ID: '
DECLARE
emp_target NUMBER := &emp_target;
cmp_target NUMBER;
empno emp_employees.emp_id%TYPE := &empno;
TYPE sal_type IS
TABLE OF emp_employees.salary%TYPE; --nested table(collection) of salary
new_sal sal_type; -- a collection variable
cnt NUMBER;
CURSOR sales_cur IS SELECT SUM(oe_orderdetails.quoted_price)
FROM oe_orderdetails
JOIN oe_orderheaders ON oe_orderdetails.order_id = oe_orderheaders.order_id
WHERE oe_orderheaders.order_date >= TO_DATE('1.1.' || 2015,'DD.MM.YYYY') AND
oe_orderheaders.order_date < TO_DATE('1.1.' || (2015 + 1),'DD.MM.YYYY'); --is it required to specify (2015 + 1) instead of 2016?
BEGIN
OPEN sales_cur;
FETCH sales_cur INTO cmp_target;
IF
cmp_target >= emp_target
THEN
UPDATE emp_employees
SET
emp_employees.salary =
CASE
WHEN emp_employees.dept_id = 10 THEN emp_employees.salary * 1.1
WHEN emp_employees.emp_id = 145 THEN emp_employees.salary * 1.15
WHEN emp_employees.dept_id = 80 THEN emp_employees.salary * 1.2
ELSE emp_employees.salary
END
WHERE emp_employees.emp_id = empno --misplaced where and end
RETURNING emp_employees.salary BULK COLLECT INTO new_sal;
END IF;
cnt := new_sal.count; --no of records in nested table
IF
cnt > 0
THEN
FOR i IN new_sal.first..new_sal.last LOOP
dbms_output.put_line('Employee ' || empno || ', new salary = ' || new_sal(i) );
END LOOP;
ELSE
dbms_output.put_line('Nobody got new salary');
END IF;
CLOSE sales_cur; -- always remember to close the cursor
END;
/

PL/SQL Function returning wrong result

I've a PL/SQL function below that's returning a wrong result in SQL Navigator and SQL Developer, but is returning the right answer in SQL Plus.
We've a script running that's executing it and returning the wrong answer too, so trying to fix it. Can anyone see any issues with it? It works fine for most people, but I've a few people going into and returning null/nothing in SQL Navigator and Developer. It's not populating l_end_date for them, and thus credits not populating.
Works fine then in SQL Plus for some reason.
create or replace function mis_get_mem_lcr_credits(p_mem_no in number) RETURN number is
--
v_lcr_credit number;
l_mem_no number;
l_start_date date;
l_end_date date;
l_dob date;
l_18th_date date;
--
cursor c1 is
select mem_no, ind_birth_dt
from cd_individual
where mem_no = l_mem_no
and pkg_mem_utils.get_member_age(mem_no,ind_birth_dt) >= 18
and nvl(ind_student_flag,'N') = 'N'
order by mem_no, ind_birth_dt;
--
cursor c2 is
select distinct m_effdt,
m_termdt
from cd$v_member_contracts9 cd1,
cd_member_product_link cd2
where cd1.mem_no = l_mem_no
and cd1.policy_no = cd2.policy_no
and cd1.m_effdt = cd2.mem_product_eff_dt --.2
and (l_18th_date between cd1.m_effdt and cd1.m_termdt OR cd1.m_effdt > l_18th_date)--.3 18 at time of contract effective date
and nvl(cd1.lapsed_to_start,'N') = 'N'
and cd2.product_id not in (14,41,31) -- Exclude No Cover, DentalProtect and HealthProtect
and cd2.product_id NOT IN (select distinct product_id
from cd_product_options
where nvl(allowed_for_lcr,'Y') = 'N')
order by cd1.m_effdt ASC;
--
begin
--
l_mem_no := p_mem_no;
v_lcr_credit := 0;
l_dob := null;
--
for crec in c1 loop
--
l_dob := crec.ind_birth_dt;
--
-- l_18th_date := substr(to_char(l_dob,'DD/MM/YYYY'),0,6)||(substr(to_char(l_dob,'DD/MM/YYYY'),7,4)+18);
if to_char(l_dob) like '29-02%' then
l_18th_date := add_months(to_date(l_dob+1),216 );
else
l_18th_date := add_months(to_date(l_dob), 216);
end if;
--
for crec2 in c2 loop
--
if crec2.m_termdt > sysdate then
--
l_end_date := sysdate;
--
else
--
l_end_date := crec2.m_termdt;
--
end if;
--
if v_lcr_credit = 0 then --earliest contract
--
if l_18th_date between crec2.m_effdt and crec2.m_termdt then
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,l_18th_date);
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
end loop;
--
end loop;
--
return round(nvl(v_lcr_credit,0));
--
end mis_get_mem_lcr_credits;
/
show errors
spool off
exit
Never, ever use to_date() on a DATE value.
to_date() converts a varchar to a date.
If you call it with a DATE the date value gets converted to a varchar which then gets converted back to a date which it was to begin with - and being subject to the evil implicit data type conversion twice in that process.
The variable l_dob is defined as DATE so you have to change
add_months(to_date(l_dob+1),216 );
...
add_months(to_date(l_dob), 216);
to
add_months(l_dob+1,216);
...
add_months(l_dob, 216);
Could be because of different values of
NLS_TERRITORY, NLS_DATE_FORMAT etc. in different environments.
So I would suggest to set explicitly these values in your script. e.g. something like EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY=''AMERICA''';
Some References:
NLS_DATE_FORMAT
NLS_TERRITORY

Calculate amounts in interval

I have the following code from Navision
PrintLine := FALSE;
LineTotalVendAmountDue := 0;
FOR i := 1 TO 5 DO BEGIN
DtldVendLedgEntry.SETCURRENTKEY("Vendor No.","Initial Entry Due Date");
DtldVendLedgEntry.SETRANGE("Vendor No.","No.");
DtldVendLedgEntry.SETRANGE("Initial Entry Due Date",PeriodStartDate[i],PeriodStartDate[i + 1] - 1);
DtldVendLedgEntry.CALCSUMS("Amount (LCY)");
VendBalanceDue[i] := DtldVendLedgEntry."Amount (LCY)";
VendBalanceDueLCY[i] := DtldVendLedgEntry."Amount (LCY)";
IF VendBalanceDue[i] <> 0 THEN
PrintLine := TRUE;
LineTotalVendAmountDue := LineTotalVendAmountDue + VendBalanceDueLCY[i];
TotalVendAmtDueLCY := TotalVendAmtDueLCY + VendBalanceDueLCY[i];
END;
I have to translate the code above into SQL server but I can't understand it. I am a newbie with Navision.
Hard if even possible to translate to SQL given no context, but essentially:
For 5 date intervals (the start date of each specified in PeriodStartDate array) add up:
SELECT SUM([Amount (LCY)])
FROM [Company$Detailed Vendor Ledg. Entry]
WHERE [Vendor No.] = $1 -- particular Vendor No.
AND [Initial Entry Due Date] BETWEEN $2 and $3 -- period start/end, inclusive
LineTotalVendAmountDue is set to the total of 5 intervals for the specific Vendor.
TotalVendAmtDueLCY is set to report grand total (presumably).