How can I change the following procedure into an Instead Of trigger? - sql

CREATE or REPLACE PROCEDURE UPDATE_SUBTOTAL is
v_order_no orderline.order_no%type;
v_subtotal number(15);
CURSOR product_orderline_cur is
SELECT ol.order_no, sum(p.unit_price * ol.qty) as subtotal
from product p, orderline ol
where p.product_no = ol.product_no
group by ol.order_no;
BEGIN
OPEN product_orderline_cur;
LOOP
FETCH product_orderline_cur into v_order_no, v_subtotal;
EXIT when product_orderline_cur%notfound;
-- store subtotal in orders table
UPDATE orders
SET subtotal = v_subtotal
WHERE order_no = v_order_no;
END LOOP;
--an order may be created but no orderlines added yet,insert a 0
UPDATE orders
SET subtotal = 0
WHERE subtotal is null;
CLOSE product_orderline_cur;
END;
/
show errors;

CREATE OR REPLACE TRIGGER orders_before_update
BEFORE UPDATE ON orders
FOR EACH ROW
DECLARE v_sum INT;
BEGIN
SELECT SUM(p.unit_price * ol.qty)
INTO v_sum
FROM product p INNER JOIN orderline ol
ON p.product_no = ol.product_no
WHERE ol.order_no = :new.order_no;
:new.subtotal := COALESCE(v_sum, 0);
END;

Related

UPDATE trigger affects all rows

I'm having trouble when I updated my values with a trigger.
Here is my trigger function
CREATE OR REPLACE FUNCTION insert_check_item()
RETURNS trigger AS
$$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO invoice_item(name, quantity, item_id, total, invoice_id)
SELECT i.name, NEW.quantity, NEW.item_id, NEW.quantity * i.price, NEW.invoice_id
FROM item i
WHERE new.item_id = i.item_id;
ELSEIF TG_OP = 'UPDATE' THEN
UPDATE invoice_item
SET total = NEW.quantity * i.price,
quantity = NEW.quantity
FROM item i
WHERE old.item_id = i.item_id;
END IF;
RETURN NULL;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER insert_total
BEFORE INSERT OR UPDATE
ON invoice_item
FOR EACH ROW
WHEN (pg_trigger_depth() < 1)
EXECUTE PROCEDURE insert_check_item();
My table invoice_item depends on item to have the name and total amount
I added this other where clause to my update and solved the problem
ELSEIF TG_OP = 'UPDATE' THEN
UPDATE invoice_item
SET total = NEW.quantity * i.price,
quantity = NEW.quantity
FROM item i
WHERE invoice_item.item_id = i.item_id AND invoice_item.invoice_item_id = NEW.invoice_item_id;
You are overcomplicating things. There is no need to replace the original INSERT or UPDATE operation if you just want to change the value of a column.
You can simply assign the needed value to the column in the NEW record.
CREATE OR REPLACE FUNCTION insert_check_item()
RETURNS trigger AS
$$
DECLARE
l_price numeric;
BEGIN
select i.price
into l_price
from item i
where i.item_id = new.item_id;
new.total := new.quantity * l_price;
RETURN new;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER insert_total
BEFORE INSERT OR UPDATE
ON invoice_item
FOR EACH ROW
EXECUTE PROCEDURE insert_check_item();

I can not understand what is this syntax error in my SQL trigger function (Postgres SQL)

I received this syntax error in Postgres SQL
ERROR: syntax error at or near ";"
LINE 41: end;
^
SQL state: 42601
Character: 1807
Far as I know, this "end" is correct in syntax
I cannot understand what wrong here?
Can anyone help me figure it out?
Here are my SQL queries in Postgres SQL
--Trigger update ave_imported_price after insert, update, delete in invoice_item
--create a view (item_id, total_imported_quantity) for later use
create or replace view item_with_quantity as
select item_id, sum(ii2.quantity) as qty
from item i2 inner join invoice_item ii2 using(item_id)
inner join item_order io using(item_id)
where i2.state = 1
group by (item_id);
--trigger function
create or replace function func_update_ave_imported_price_when_change_invoice_item() returns trigger as
$$ declare
var_qty int; --total qty
begin
if TG_OP = 'insert' then
select qty into var_qty from item_with_quantity where item_id = new.item_id;
-- update ave_imported_price
-- new_ave_price = ((old_total_qty*old_ave_price)+ (new.qty*new.imported_price)) / new_total_qty
update item set ave_imported_price = ((var_qty*ave_imported_price)
+ (new.quantity*new.imported_price)) / (var_qty + new.quantity)
where item.item_id = new.item_id;
return new;
else if TG_OP = 'update' then
select qty into var_qty from item_with_quantity where item_id = new.item_id;
-- update ave_imported_price
-- updated_ave_price = ((old_total_qty*old_ave_price) - (old.qty*old.imported_price)
-- + (new.qty*new.imported_price)) / (old_total_qty - old.qty + new.qty)
update item set ave_imported_price = ((var_qty*ave_imported_price) - (old.quantity*old.imported_price)
+ (new.quantity*new.imported_price)) / (var_qty - old.quantity + new.quantity)
where item.item_id = new.item_id;
return new;
else if TG_OP = 'elete' then
select qty into var_qty from item_with_quantity where item_id = new.item_id;
-- update ave_imported_price
-- ave_price = ((old_total_qty*old_ave_price) - (old.qty*old.imported_price)) / new_total_qty
update item set ave_imported_price = ((var_qty*ave_imported_price)
- (old.quantity*old.imported_price)) / (var_qty - old.quantity)
where item.item_id = new.item_id;
return old;
end if;
end;
$$ language plpgsql;

Making procedure to insert/update qty. What am I doing wrong?

I am trying to make procedure that count total slab # of from each pallet from prod_result table and insert or update that count to qty column in plt_result table that has same pallet #(plt_no)
I've tried insert, update both but doesn't work.
create or replace PROCEDURE update_qty
is
v_plt_no nvarchar2(20);
v_qty number;
cursor q1 is
select count(slab_no)
into v_qty
from prod_result
where plt_no = v_plt_no;
begin
if v_qty > 0 then
update plt_result
set qty = 'v_qty'
where plt_no = v_plt_no;
end if;
end;
What am I doing wrong here? Help please.
I'm not sure which version you're looking for - is it the one that loops through all PLT_NOs in a table, or the one that should accept PLT_NO as a parameter. Here are both options, see whether any of them helps.
The first one uses cursor FOR loop; it is easier to maintain as Oracle does most of the dirty job for you (opening the cursor, fetching, exiting the loop, closing the cursor).
create or replace procedure update_qty
is
begin
for cur_r in (select plt_no, count(slab_no) v_qty
from prod_result
group by plt_no
)
loop
if cur_r.v_qty > 0 then
update plt_result r set
r.qty = cur_r.v_qty
where r.plt_no = cur_r.plt_no;
end if;
end loop;
end;
The second one accepts PLT_NO as a parameter:
create or replace procedure update_qty
(par_plt_no in prod_result.plt_no%type)
is
v_qty number;
begin
select count(p.slab_no)
into v_qty
from prod_result p
where p.plt_no = par_plt_no;
if v_qty > 0 then
update plt_result r set
r.qty = v_qty
where r.plt_no = par_plt_no;
end if;
end;
Something like the following, where you actually open and fetch from the cursor. You'll have to assign a value to v_plt_no for the cursor to fetch anything.
create or replace PROCEDURE update_qty
is
v_plt_no nvarchar2(20);
v_qty number;
cursor q1 is
select count(slab_no)
into v_qty
from prod_result
where plt_no = v_plt_no;
begin
OPEN q1;
FETCH q1 INTO v_qty;
CLOSE q1;
if v_qty > 0 then
update plt_result
set qty = 'v_qty'
where plt_no = v_plt_no;
end if;
end;

Invoke a function from a procedure

I am trying to create a PL/SQL procedure that will invoke a function I called GET_HIGHORDER_FUNC which is already working:
create or replace FUNCTION GET_HIGHORDER_FUNC
return number
AS
c_hiorder number;
BEGIN
select max(sum(product.product_standardprice * orderline.ordered_quantity)) into c_hiorder
from customer, product, orderline, orders
where customer.customer_id = orders.customer_id
and orders.order_id = orderline.order_id
and orderline.product_id = product.product_id
group by customer_name;
RETURN c_hiorder;
END GET_HIGHORDER_FUNC;
create or replace procedure PRINT_CUST_PROC(
p_hiordername in number)
as
begin
/* This procedure should show the name of the customer who have the highest
amount of order which will be available upon invoking the function above */
end;
Just invoke it as you would with any function?
This is how i would try based on your latest comments
create or replace FUNCTION GET_HIGHORDER_FUNC(c_customer_name out varchar2,c_hiorder out int)
return number
AS
c_hiorder number;
BEGIN
select y.customer_name
,y.summed_price
into customer_name
,c_hiorder
from (
select x.customer_name
,x.summed_price
,row_number() over(order by x.summed_price desc) as rnk
from (
select customer_name
,sum(product.product_standardprice * orderline.ordered_quantity) as summed_price
from customer, product, orderline, orders
where customer.customer_id = orders.customer_id
and orders.order_id = orderline.order_id
and orderline.product_id = product.product_id
group by customer_name
)x
)y
where y.rnk=1;
RETURN 1;
END GET_HIGHORDER_FUNC;
create or replace procedure PRINT_CUST_PROC(
p_hiordername in number)
as
l_return int;
l_customer_name varchar2(1000);
l_hiorder int;
begin
/* This procedure should show the name of the customer who have the highest
amount of order which will be available upon invoking the function above
*/
l_return : = GET_HIGHORDER_FUNC(l_customer_name,l_hiorder);
dbms_output.put_line(l_customer_name);
dbms_output.put_line(l_hiorder);
end;

SQL Trigger Update is ALWAYS null

We are trying to make a trigger to calculate the TOTAL_COST of a Car Rental based on the number of days it has been rented. The total cost for the rental is calculated based on the number of days and the cost of the vehicle. An additional tax of 12% is added to the total. If the rental is longer than 10 days a discount of 15% is subtracted from the total cost.
This Is Our Trigger:
create or replace trigger L5_Q8
After Update on E2_RESERVATIONS
for each row
Declare
TOTALDAYS NUMBER(4);
TotalCostBeforeTax Number(8);
BEGIN
TOTALDAYS := (trunc(:NEW.END_DATE) - TRUNC(:NEW.START_DATE)) + 1;
IF(TOTALDAYS > 10) THEN
SELECT V.COST_PER_DAY * TOTALDAYS * 0.85
INTO TotalCostBeforeTax
from E2_Reservations R
join E2_Vehicle V on R.V_ID = V.V_ID;
END IF;
IF(TOTALDAYS <= 10) THEN
SELECT V.COST_PER_DAY * TOTALDAYS
INTO TotalCostBeforeTax
from E2_Reservations R
join E2_Vehicle V on R.V_ID = V.V_ID;
END IF;
TotalCostBeforeTax := TotalCostBeforeTax * 1.12;
UPDATE E2_RESERVATIONS SET TOTAL_COST = 10.1 WHERE ROWID IN (SELECT MAX(ROWID) FROM E2_RESERVATIONS);
END;
These are Our Tables:
E2_RESERVATIONS:
E2_VEHICLE:
You're trying to calculate the total cost for the current record, right? So do the calculation before the change gets applied, not in an after update trigger. Also, there's way too much SQL in that trigger.
Try this:
create or replace trigger l5_q8
before update on e2_reservations
for each row
declare
totaldays number(4);
totalcostbeforetax number(8,2);
begin
-- length of reservation
totaldays := (trunc(:new.end_date) - trunc(:new.start_date)) + 1;
-- base cost of hire
select v.cost_per_day * totaldays
into totalcostbeforetax
from e2_vehicle v
where v.v_id = :new.v_id;
-- apply discount for long reservation
if(totaldays > 10) then
totalcostbeforetax := totalcostbeforetax * 0.85;
end if;
-- apply tax to total cost
:new.total_cost := totalcostbeforetax * 1.12;
end;
Note that is a BEFORE UPDATE trigger, not AFTER UPDATE.
Also, the variable totalcostbeforetax needs to handle decimals, because you're multiplying by 0.85, and anyway should match the declaration of the column total_cost.
"Wouldn't you need to join the vehicle table with reservations?"
A trigger has access to all the columns of the trigger it is built on. So you're already joined to the RESERVATIONS table, by dint of where v.v_id = :new.v_id, which is the vehicle ID of the current reservations record.