Trigger with table join - sql

I want to create a trigger to update stock inventory. My quantity on stock and quantity ordered by the client are on two different tables. How do i join both?
CREATE OR REPLACE
TRIGGER UPDATE_QUANTITY_TRIGGER
AFTER insert ON SALE_ORDER
FOR EACH ROW
BEGIN
update product values
(:old.product_id,
:old.product_name,
:old.description,
quantity_on_stock-s.quantity,
:old.minimal_quantity,
:old.unit_price,
:old.product_type_id);
from product, sale_order s
where product.product_id=s.product_id;
END;
OR
CREATE OR REPLACE
TRIGGER UPDATE_QUANTITY_TRIGGER
AFTER insert ON SALE_ORDER
FOR EACH ROW
BEGIN
update product set
p.quantity_on_stock= p.quantity_on_stock-s.quantity;
from product p , sale_order s
where p.product_id=s.product_id;
END;
The sale_order table has columns sale_no, quantity, and product_id.

You can't join in an update statement. Your second attempt is closer but is still joining, and has no correlation. You also have a stray semicolon. I think you're looking for something like:
CREATE OR REPLACE
TRIGGER UPDATE_QUANTITY_TRIGGER
AFTER insert ON SALE_ORDER
FOR EACH ROW
BEGIN
update product p set
p.quantity_on_stock = p.quantity_on_stock - :new.quantity
where p.product_id = :new.product_id;
END;
This uses the values from the :new pseudorecord to both identify the product record to be updated, and to get the quantity to decrement the stock level by. There is no need to join to the triggering table again - which isn't allowed anyway.
You may run into unexpected behaviour or results doing this kind of update in a multiuser environment though.

Related

SQL insert trigger for insert with update

I have two tables:
[1] Donations - with amount and pet_id field
[2] Pets - with id and total donations field
I'm trying to create a trigger which will update total donations field whenever
a new row is being inserted to Donations table. I tried this one:
create trigger update_donations
on sponserships
for insert
as
update dbo.Pets
set tot_donations = (
select new_val = inserted.amount + pets.tot_donations
from inserted
where inserted.[Pet-ID] = pets.[Animal-ID]
)
But of course it changes all records, whereas i want to change only records that are changed in the donations table.
It is usually not a good practice to store this type of derived information - you could have a view that computes it on the fly rather than a trigger that keeps it up to date. Also please note that if you go that way, you also need a delete and an update trigger...
That said, you can use the following query in your insert trigger to update the relevant record in the pets table:
update p
set p.total_donations = p.total_donations + i.amount
from dbo.Pets p
inner join inserted i on i.[Pet-ID] = p.[Animal-ID]

Add data to trigger from the last insert

I am faced with the following situation:
I created a trigger which reacts on insert to the third table. When I insert any data (for example 1 1 2), last number should be subtracted from the Amount of stock column from cell, which has necessary ID Product (as it's shown on picture). But how can I understand which row was the last added? I thought firstly to do it by select, but it seems impossible. And now I think that it's possible to do it with the help of cursor, but it doesn't seem as the best variant. Is there a better variant how can I do it?
Here's my code of trigger, but it only subtracts 1 from the 1st product each time, unfortunately:
CREATE TRIGGER AmountInsert ON Amount
AFTER INSERT
AS
BEGIN
UPDATE Product
SET Amount_On_Stock =
(SELECT Amount_On_Stock FROM Product
WHERE ID_Product = 1) - 1
WHERE ID_Product = 1
END
The first thing you need to understand is that in a trigger in SQL Server you are provided with an inserted pseudo-table and a deleted pseudo-table. You use these tables to determine what changes have occurred.
I think the following trigger accomplishes what you are looking for - the comments explain the logic.
CREATE TRIGGER dbo.AmountInsert ON dbo.Amount
AFTER INSERT
AS
BEGIN
set nocount on;
update P set
-- Adjust the stock level by the amount of the latest insert
Amount_On_Stock = coalesce(Amount_On_Stock) - I.Amount
from dbo.Product P
inner join (
-- We need to group by ID_Product in case the same product appears in the insert multiple times
select ID_Product, sum(Amount) Amount
from Inserted
group by ID_Product
-- No need to update is net change is zero
having sum(Amount) <> 0
) I
on I.ID_Product = P.ID_Product;
END

SQL After trigger update value that triggers update

I have 2 tables, shipment and invoice. I have an after trigger that updates the shipment table with the invoice number from the invoice table.The invoice table has their related shipment numbers, can be more than one for each invoice.
Create Trigger ShipmentInvoice
on Therefore.dbo.Invoice
For Insert
AS
Update cat set
invoice = Fac.invoice
FROM therefore.dbo.thecat10 cat
INNER JOIN therefore.dbo.vFacturaAlbaran alb
on alb.shipment COLLATE Modern_Spanish_CI_AS = alb.shipment
This Works but it updates the whole table every time. I need a way to update the shipment table only with the values that were recently added in the invoice table when the trigger is activated.
Edit
The trigger is over the invoice table. I am updating the shipment table with the invoice numbers from the invoice table.
This Works but it updates the whole table every time. I need a way to update the shipment table only with the values that were recently added in the invoice table when the trigger is activated.
That because you are joining both tables, you need to use INSERTED table to get only the inserted rows as :
Create Trigger ShipmentInvoice
on Therefore.dbo.Invoice
For Insert
AS
Update cat set
invoice = I.invoice
FROM therefore.dbo.thecat10 cat
INNER JOIN INSERTED I alb ON Cat.ID = I.ID
...
it seems odd that you are updating the table to you are already inserting into; why are you not resolving the correct value before hand? This is pseudo-sql, but will get you on the right path
CREATE TRIGGER ShipmentInvoice
ON Therefore.dbo.Invoice
FOR INSERT
AS
UPDATE cat
SET invoice = Fac.invoice --There is no table/alias Fac, where is this coming from? inserted?
FROM therefore.dbo.thecat10 cat
JOIN inserted i ON cat.{Primary/Foreign ID COLUMN} = i.{Primary/Foreign ID COLUMN}
INNER JOIN therefore.dbo.vFacturaAlbaran alb ON alb.shipment COLLATE Modern_Spanish_CI_AS = alb.shipment;
Note the items in the braces ({}) and also my comment about your object Fac.

How can I update and replicate one column from one table to another column from another table?

I need to replicate one column (TYPE) from one table (CUTOMER) to another column (UNDEF000) from table (ORDERS), by this way everytime when someone update column(TYPE) to be automaticaly replicated on (UNDEF000), Table CUSTOMER and ORDERS are linked by column (PRE_ORDERCODE).
First I try to fill UNDEF000 from TYPE:
UPDATE ORDERS
JOIN CUSTOMER
SET ORDERS.UNDEF000=CUSTOMER.TYPE
WHERE ORDERS.PRE_ORDERCODE= CUSTOMER.PRE_ORDERCODE;
not function :(
UPDATE ORDERS
SET ORDERS.UNDEF000= CUSTOMER.TYPE
FROM CUSTOMER CUSTOMER
INNER JOIN ORDERS ORDERS
ON CUSTOMER.PRE_ORDERCODE= ORDERS.PRE_ORDERCODE
Can you please help me with this two problems?
I think your update should be like:
UPDATE ORDERS O SET O.UNDEF000= (
SELECT CUSTOMER.TYPE FROM CUSTOMER
WHERE CUSTOMER.PRE_ORDERCODE = O.PRE_ORDERCODE);
Trigger code:
create or replace trigger after_update_customer after update on
customer for each row
declare
begin
update orders set UNDEF000 = :new.type
where pre_ordercode = :new.pre_ordercode;
end;

Oracle Triggers Update based on orderlines fulfilled how to avoid mutation error

for a typical products & shipping Database I am exploring the best way to run a trigger that:
When an order line is set to 'Complete', a trigger is ran that:
Looks for any other order lines for that order.
If all other order lines for that order are also 'Complete'
Update the order header table to complete.
For clatiry: The order header table would store the overall oder total, and the orderLines table stores each product of the order.
SO far, the trigger is written as such:
CREATE OR REPLACE TRIGGER orderComplete
after update ON orderline
for each row
WHEN (new.orderline_fulfilled = 'Y')
DECLARE count NUMBER := 5;
ordersNotDone NUMBER;
BEGIN
SELECT COUNT(Orderline_fulfilled) INTO ordersNotDone
FROM orderHeader
JOIN orderline ON
orderHeader.Order_id = orderLine.Orderline_order
WHERE Order_id = :old.orderline_order
AND orderline_fulfilled = 'Y';
IF ordersNotDone = 0
THEN
UPDATE orderHeader
SET completed = SYSDATE
WHERE orderId = :old.orderline_order;
ENDIF;
END;
This above causes the mutation error, when updating the orderline row.
Enforcing integrity with a trigger is inherently problematic because the RDBMS read consistency mode allows multiple changes simultaneously that cannot see each others' result.
A better solution might be to avoid denormalising the data, and rely on detecting the presence of an incomplete order line to identify incomplete orders. As this would be the minority of cases it can be optimised with a function-based index along the lines of:
create index my_index on orderline(case orderline_complete when 'NO' then orderid else null end)
This will index only the values of orderline where orderline_complete is 'NO', so if there are only 100 such rows in the table then the index will only contain 100 entries.
Identifying incomplete orders is then a matter only of a full or fast full index scan of a very compact index with a query:
select distinct
case orderline_complete when 'NO' then orderid else null end orderid
from
orderline
where
case orderline_complete when 'NO' then orderid else null end is not null;
If you are using 11g then check about compound triggers, example: http://www.toadworld.com/KNOWLEDGE/KnowledgeXpertforOracle/tabid/648/TopicID/TRGC1/Default.aspx
The simplest answer is to use a slightly different type of trigger which is triggered not after a row is updated but after the table is updated. This will not suffer from this problem.
So do something like:
CREATE or REPLACE TRIGGER trigger_name
AFTER INSERT ON orderline --note no for each row
BEGIN
--loop over all orders which contain no unfulfilled orders
FOR lrec IN (SELECT order_id FROM orderline group by order_id where not exists (select 1 from orderline where orderline_fulfilled = 'Y'))
LOOP
-- do stuff to order id because this are complete
END LOOP;
END;
So here we may have completed multiple orders on the insert so the trigger needs to cope with this. Sorry I do not have an oracle instance to play with at home. Hope this helps