sql ibm db2 trigger after insert multiple table - sql

I have three table related in a trigger
1) Order_Item -- Recipe_ID, Order_ID, Quantity
2) Ingredient_Recipe -- Stock_ID, Recipe_ID, Quantity
3) Stock -- Stock_ID,Balance
Another table related,
4) Recipe -- Recipe_ID, Name
Trying to make a trigger that Stock will decrease after Order_Item insert, also based on the quantity in Ingredient_Recipe.
The basic formula is
Balance (from Stock) - ( Quantity (from Ingredient_recipe ) * Quantity (from Order_Item) )
I created the trigger
CREATE TRIGGER Stck_Up
AFTER INSERT ON Order_Item
REFERENCING NEW AS N
FOR EACH ROW mode db2sql
UPDATE Stock
SET Balance = (SELECT sum (Stock.Balance - (Quantity *( SELECT Quantity FROM Ingredient_Recipe
where Ingredient_Recipe.Recipe_ID = Order_Item.Recipe_ID)))
FROM Stock
WHERE Stock_ID = N.Stock_ID )
WHERE Stock_ID = N.Stock_ID;
but the error is N.Stock_ID is not valid

Related

Trigger after insert with nested if conditions pl sql

I want to create a trigger after there's an insert in 'order_items' table. The trigger is to minus the quantity of that product id in 'inventories' table by the quantity being insert to 'order_items' table. If the result of quantity after being subtracted is negative, then the quantity of 'inventories' will be changed back to the old quantity and the data in 'order_items' will be deleted (the insertion is failed). If the product id is in many warehouses, then just minus the quantity with the smallest warehouse_id
Example #1:
Product ID '101' is available in Warehouse ID '1','2','3'
So choose the product id '101' in warehouse id '1' (smallest)
Product ID '101' Quantity in Inventories = 10
Product ID '101' Quantity Order_Items = 15
So 10-15=-5 (negative)
So...
Product ID '101' Quantity in Inventories = 10 (back to old data)
Delete '101' datas from Order_Items
Example #2:
Product ID '102' Quantity in Inventories = 20
Product ID '102' Quantity Order_Items = 8
So 20-8=12
So...
Product ID '102' Quantity in Inventories = 12
This is my code so far, any ideas? Thanks!
CREATE OR REPLACE TRIGGER update_qty
AFTER INSERT
ON order_items
FOR EACH ROW
DECLARE
qty_diff number;
qty_in number;
total number;
BEGIN
select quantity,count(product_id) into qty_in,total
from inventories
where product_id=:new.product_id;
if(total>1) then
select product_id from inventories where warehouse_id=(select min(warehouse_id) from inventories);
else
qty_diff:=qty_in-:new.quantity;
if(qty_diff<0) then
i.quantity:=:old.quantity;
delete from order_items where product_id=:new.product_id;
else
update inventories
set quantity=qty_diff;
end if;
end if;
END;
/
BEGIN
insert into order_items(order_id,item_id,product_id,quantity,unit_price) values(12345,12345,12345,100,200);
END;
/
It is better to apply this logic at the time of insertion(using procedure in DB or application-level logic). but if you are doing it for learning purposes and if I followed you correctly, You can use the following simple update in the trigger.
CREATE OR REPLACE TRIGGER UPDATE_QTY AFTER
INSERT ON ORDER_ITEMS
FOR EACH ROW
BEGIN
-- check and update the INVENTORIES table
-- lowest warehouse id with the product will be selected
-- new quantity or more must be available in the INVENTORIES table
UPDATE INVENTORIES I
SET
QUANTITY = QUANTITY - :NEW.QUANTITY
WHERE QUANTITY >= :NEW.QUANTITY
AND PRODUCT_ID = :NEW.PRODUCT_ID
AND NOT EXISTS (
SELECT 1
FROM INVENTORIES II
WHERE II.PRODUCT_ID = :NEW.PRODUCT_ID
AND II.WAREHOUSE_ID < I.WAREHOUSE_ID
);
-- if no record is selected means product is not available in any of the lowest warehouse
IF SQL%ROWCOUNT = 0 THEN
RAISE_APPLICATION_ERROR(
-20000,
'product not available in inventory'
);
END IF;
END;
/

The aggregate expression cannot be used in the WHERE clause

I have the following tables in my database:
The first table is named Amount, second Product, third Purchase.
And I should to create the trigger on insert to amount table. For example, I'll insert the following values: 4, 1, 10, where 4 is id_purchase, 1 is id_product and 4 is amount of this products. And trigger should subtract this amount from Amount_On_Stock. In my example, it should be: was 48, became 38.
Here's the code of my trigger:
CREATE TRIGGER AmountInsert ON Amount
AFTER INSERT
AS
BEGIN
UPDATE Product
SET Amount_On_Stock = (
SELECT
Amount_On_Stock
FROM Product
WHERE ID_Product = (
SELECT
MAX(ID_Product)
FROM Purchase
WHERE ID_Purchase = (
SELECT
MAX(ID_Purchase)
FROM Purchase
)
)
)-(
SELECT
Amount
FROM AMOUNT
WHERE ID_Product = (
SELECT
MAX(ID_Product)
FROM Purchase
WHERE ID_Purchase = (
SELECT
MAX(ID_Purchase)
FROM Purchase
)
)
)
END
But when I try to create this trigger I have the following error:
The aggregate expression cannot be used in the WHERE clause unless it
is contained in a subquery of the HAVING clause or in the select list,
and the column being aggregated is not an external reference.
So, how can I solve this problem?
Your trigger looks nothing like a SQL Server trigger. I would expect your trigger to look more like this:
CREATE TRIGGER AmountInsert ON Amount AFTER INSERT
AS
BEGIN
UPDATE p
SET Amount_On_Stock = p.Amount_On_Stock - i.amount
FROM Product p JOIN
inserted i
ON p.ID_Product = i.ID_Product;
END;
However, this will not do the right thing if you have multiple inserts on the same product at the same time. To handle that you need aggregation:
CREATE TRIGGER AmountInsert ON Amount AFTER INSERT
AS
BEGIN
UPDATE p
SET Amount_On_Stock = p.Amount_On_Stock - i.amount
FROM Product p JOIN
(SELECT i.ID_Product, SUM(i.amount) as amount
FROM inserted i
GROUP BY i.ID_Product
) i
ON p.ID_Product = i.ID_Product;
END;

How to delete rows that fail sqlite constraint?

I have a table that looks like this:
orders (
user_id INTEGER
item_id INTEGER
quantity INTEGER
... (more columns and constraints)
CHECK(quantity > 0)
)
I want to decrease the quantity of an order by one, and delete it if that would make the quantity zero.
Is it possible to do this in one statement?
Right now I have:
UPDATE orders SET quantity = quantity - 1 WHERE *blah blah complicated clause*
However when the quantity is 1, this fails and leaves the quantity at 1, because setting it to 0 would be a constraint error.
I want it to instead just delete the row when the quantity is 1, because the order is now empty. How can I do this?
I'd suggest deleting rows, according to the complicated clause with AND quantity < 2 prior to doing the UPDATE.
e.g. (where user_id = 1 represents the complicated clause)
DROP TABLE IF EXISTS orders;
CREATE TABLE IF NOT EXISTS orders (user_id INTEGER, order_id INTEGER, quantity INTEGER, CHECK(quantity > 0) );
INSERT INTO orders VALUES (1,1,10),(1,2,1),(1,3,2),(2,1,3),(2,2,2),(2,3,5);
SELECT * FROM orders;
DELETE FROM orders WHERE user_id = 1 AND quantity < 2;
UPDATE orders SET quantity = quantity -1 WHERE user_id = 1;
SELECT * FROM orders;
results in (all rows before anything is done) :-
and then :-
i.e. orders 1 and 3 have been updated whilst order 2 (circled in above) has been deleted.

Operation on each row from select query

How execute additional query (UPDATE) on each row from SELECT?
I have to get amount from each row from select and send it to user's balance table.
Example:
status 0 - open
status 1 - processed
status 2 - closed
My select statement:
select id, user_id, sell_amount, sell_currency_id
from (select id, user_id, sell_amount, sell_currency_id,
sum(sell_amount)
over (order by buy_amount/sell_amount ASC, date_add ASC) as cumsell
from market t
where (status = 0 or status = 1) and type = 0
) t
where 0 <= cumsell and 7 > cumsell - sell_amount;
Select result from market table
id;user_id;amount;status
4;1;1.00000000;0
6;2;2.60000000;0
5;3;2.00000000;0
7;4;4.00000000;0
We get 7 amount and send it to user balance table.
id;user_id;amount;status
4;1;0.00000000;2 -- took 1, sum 1, status changed to 2
6;2;0.00000000;2 -- took 2.6, sum=3.6, status changed to 2
5;3;0.00000000;2 -- took 2, sum 5.6, status changed to 2
7;4;2.60000000;1 -- took 1.4, sum 7.0, status changed to 1 (because there left 2.6 to close)
User's balance table
user_id;balance
5;7 -- added 7 from previous operation
Postgres version 9.3
The general principle is to use UPDATE ... FROM over a subquery. Your example is too hard to turn into useful CREATE TABLE and SELECT statements, so I've made up a quick dummy dataset:
CREATE TABLE balances (user_id integer, balance numeric);
INSERT INTO balances (user_id, balance) VALUES (1,0), (2, 2.1), (3, 99);
CREATE TABLE transactions (user_id integer, amount numeric, applied boolean default 'f');
INSERT INTO transactions (user_id, amount) VALUES (1, 22), (1, 10), (2, -10), (4, 1000000);
If you wanted to apply the transactions to the balances you would do something like:
BEGIN;
LOCK TABLE balances IN EXCLUSIVE MODE;
LOCK TABLE transactions IN EXCLUSIVE MODE;
UPDATE balances SET balance = balance + t.amount
FROM (
SELECT t2.user_id, sum(t2.amount) AS amount
FROM transactions t2
GROUP BY t2.user_id
) t
WHERE balances.user_id = t.user_id;
UPDATE transactions
SET applied = true
FROM balances b
WHERE transactions.user_id = b.user_id;
The LOCK statements are important for correctness in the presence of concurrent inserts/updates.
The second UPDATE marks the transactions as applied; you might not need something like that in your design.

Searching for a row with a certain insert value, then comparing a value in that row with an insert value

I am trying to create an update trigger that checks the quantity of a product in stock over how much someone is ordering and displays a message if not enough of that product is in stock.
It's letting me create the trigger but when testing, it displays an error "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=,...."
I'm not really understanding how it's returning more than one value as I have it searching for the particular row with the product ID that matches the inserted value first. I then have it comparing the UnitsInStock from the inserted value.
Here's what I have so far:
CREATE TRIGGER tr_check_qty
ON OrderDetails
FOR UPDATE
AS
DECLARE #ProductID int,
#Quantity int
SELECT #ProductID = ProductID,
#Quantity = Quantity
FROM inserted
WHERE #ProductID = ( SELECT ProductID FROM Products )
IF
#Quantity > ( SELECT UnitsInStock FROM Products )
BEGIN
PRINT 'Not enough product in stock'
ROLLBACK TRANSACTION
END
I think you want something like:
CREATE TRIGGER tr_check_qty
ON OrderDetails
FOR UPDATE
AS
IF EXISTS (
SELECT *
FROM
Products p
inner join
inserted i
on p.ProductID = i.ProductID
WHERE i.Quantity > p.UnitsInStock)
BEGIN
PRINT 'Not enough product in stock'
ROLLBACK TRANSACTION
END
However, I'm a bit mystified on why this is inside an update trigger, as compared to an insert trigger.
SELECT UnitsInStock FROM Products as well as SELECT ProductID FROM Products may return a whole column, not a single value. You should specify some restriction there, like WHERE id = #someId.
You have more the one products right?
Then this line:
WHERE #ProductID = ( SELECT ProductID FROM Products )
And this line:
#Quantity > ( SELECT UnitsInStock FROM Products )
will return many rows.
Note as well that if you insert more then one row. The inserted table will have more then one row as well
You might want have to do something like this:
IF EXISTS
(
SELECT
NULL
FROM
inserted
WHERE EXISTS
(
SELECT
NULL
FROM
Product
WHERE
Product.Quantity>inserted.Quantity
)
)
BEGIN
PRINT 'Not enough product in stock'
ROLLBACK TRANSACTION
END