Insert or Replace with sum of old values - sql

I am building Inventory Application using PhoneGap.In that i have one module STOCK for stock management.
Stock Table Query
CREATE TABLE STOCK (
sto_id INTEGER PRIMARY KEY AUTOINCREMENT,
pro_id INTEGER FOREIGNKEY REFERENCES PRODUCT(pro_id) UNIQUE,
quantity TEXT
)
INSERT OR REPLACE Query
INSERT OR REPLACE INTO STOCK (pro_id,quantity) VALUES ("1","5");
There is not a single issue with this query its working perfectly but i want to update SUM of OLD VALUES WITH NEW ONE.
Example:
pro_id quantity
1 5
This is existing record so now when i will fire above query for new transaction which have 3 quantity then quantity should be (5 (old) + 3 (new) ) = 8.
So after updateing record it looks like.
pro_id quantity
1 8
How can i solve this any idea. or let me know if i am on wrong way.
Thanks.

Actually I am not real founder of this solution. The real hero is Daverandom with the help of him i have solve my issue.
He has suggest me to follow this solution and that is really helpful to find my solution.
Older query looks like as
INSERT OR REPLACE INTO STOCK (pro_id,quantity) VALUES ("1","5");
New Query looks like as
INSERT OR IGNORE INTO STOCK (pro_id,quantity) VALUES (1,0) ;
UPDATE STOCK SET quantity = quantity + 2 WHERE pro_id=1;
Update:
If you will not add WHERE pro_id="val" in UPDATE then it will UPDATE all rows.
So that will generate will appropriate result.
When user fire query first time then quantity will be 2 and when you fire same query second time it will 4.
So, We can change that value in each update.
Again thanks to Daverandom.

In SQLite, you can create a trigger to handle such functionality:
CREATE TRIGGER t1 BEFORE INSERT ON STOCK WHEN NEW.pro_id IN (SELECT pro_id FROM STOCK) BEGIN
UPDATE STOCK SET quantity=COALESCE(NEW.quantity, 0)+COALESCE((SELECT quantity FROM STOCK WHERE pro_id=NEW.pro_id), 0) WHERE pro_id=NEW.pro_id;
SELECT RAISE(IGNORE); -- Ignore INSERT
END;
From now on, whenever you try to insert an existing pro_id, update on quantity is done instead. Conflict clause (OR REPLACE) doesn't matter as trigger will handle it (for pro_id).
Yet another, this time without triggers, using single statement solution:
INSERT OR REPLACE STOCK(pro_id, quantity)
SELECT npro_id, COALESCE(nqty, 0)+COALESCE(quantity,0) FROM (
SELECT 123 AS npro_id, 9999 AS nqty
) LEFT JOIN STOCK ON npro_id=pro_id;
Just replace 123 with new prod_id and 9999 with new quantity.

Related

Trigger keeps updating value in every row instead of particular one

I am trying to calculate a sum for each particular order. I am using this trigger but it doesn't work properly, it updates every row with the same value instead of the only one with proper id.
done_services table
id
service_id
price
service table
id
name
payment table
id
sum
service_id
CREATE FUNCTION make_sum() RETURNS TRIGGER
AS $$
BEGIN
UPDATE payment
SET sum = (select sum(price) from done_services where service_id = new.service_id);
RETURN NULL;
END;$$ LANGUAGE plpgsql;
CREATE TRIGGER make_sum
AFTER INSERT ON basket FOR EACH ROW EXECUTE FUNCTION make_sum();
I used this command to enter an item
insert into done_services(id, service_id, price) values(uuid_generate_v4(), '76594d2f-7153-495f-9671-0ddaa331568c', 100);
But the sum changed for both rows instead of the only one with service id
Image
The immediate cause for the error message is the missing WHERE clause as instructed by Edouard. Plus, prevent expensive empty updates like:
UPDATE payment p
SET sum = ds.sum_price
FROM (
SELECT sum(d.price) AS sum_price
FROM done_services d
WHERE d.service_id = NEW.service_id
) ds
WHERE p.service_id = sum_price
AND p.sum IS DISTINCT FROM ds.sum_price;
In addition to fixing the prime error, this prevents empty updates that would not change the sum, but still write a new row version at full cost.
But the whole idea is questionable.
Keeping a sum from many rows up to date via trigger is expensive and error prone. Did you cover DELETE and INSERT accordingly? What about TRUNCATE? What about concurrent write access? Race conditions, deadlocks?
To get get the current sum for a set that can change dynamically, the superior solution is typically not to save that sum in the table at all. Use a VIEW or MATERIALIZED VIEW instead.
Or, to get the sum for a single or few payments, use a function:
CREATE OR REPLACE FUNCTION f_payment_sum(_service_id int)
RETURNS numeric
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT sum(d.price)
FROM done_services d
WHERE d.service_id = _service_id;
$func$
Related:
Updating a row based on a value from another table?
just missing something in your UPDATE statement :
UPDATE payment
SET sum = (select sum(price) from done_services where service_id = new.service_id)
WHERE service_id = new.service_id ;
Next time please create a dbfiddle with your data model, sample of data and queries.

I have a table where I need to update or insert depending on field paramaters

I have spent many hours researching this problem and trying various solutions but I never quite find a suitable solution for my specific problem. I am new to SQL and some of the examples are confusing as well.
So here is my dilemma. I have a equipment table that tracks oil changes for specific units in a database. The table looks like this:
**id UnitID Posted_On Date_Completed Note OverDueBy**
1 BT-109F 2019-02-04 2019-02-14 Hrs Overdue 23
1 BT-108G 2020-01-17 2020-01-22 Days Overdue 12
1 BT-122K 2020-01-02 2020-01-16 Days Overdue 12
1 BT-109F 2019-02-04 Days Overdue 3
The example records above need to be created or updated by the query. The date completed is entered manually by the technician when he has completed the oil change.
What I want the query to do is, Check to see if a specific Unit has a record where the 'Date_Completed' field is empty, and if so update the 'OverDueBy' field to reflect the new value. If all the records for the specified Unit have the 'Date_Completed' fields filled in, then the query should create a new record will all fields filled in except for the 'Date_Completed' field.
Can anyone help me construct such a query?
Thanks
Clan
First create a unique partial index for the column UnitID:
CREATE UNIQUE INDEX idx_unit ON tablename(UnitID)
WHERE Date_Completed IS NULL;
so that only 1 row with Date_Completed=null is allowed for each UnitID.
So a statement like this:
INSERT INTO tablename(id, UnitID, Posted_On, Date_Completed, Note, OverDueBy)
VALUES (?, 'BT-109F', ?, null, ?, ?)
ON CONFLICT(UnitID) WHERE Date_Completed IS NULL DO UPDATE
SET OverDueBy = ?;
will insert the new values only if there is no row already for UnitID='BT-109F' with null in Date_Completed.
But if there is such a row then it will update the column OverDueBy.
I'm not sure what values you want to insert or what will be the updated value so replace the ? with the appropriate values.
Firstly I would use a view rather than a table to store any calculated data - it reduces storage overheads and will update the calculation every time the view is opened.
If you're using SQLite you should be able to get the overdue by subtracting the Posted_On from its function to return today's date something like date('now') or julianday('now') - read up on and test the functions to ensure it does what you want.
So along the lines of:-
create view MyView as select *, julianday('now') - julianday(Posted_On) as OverDueBy from ClansTable where Date_Completed is null;
If you want to store a snapshot you can always create a table from a view in any case:-
create table MyStoredOverduesOn4thFeb as select * from MyView;
You can find your units that have all Date_Completed and create a single new record like so:-
Create table CompletedUnits as select id, UnitID, max(posted_on) as latest_posted_on, '' as Date_Completed from ClansTable group by id, UnitID having count(*) = count(Date_Complete);
Test this SQL and see if you can get it working - note I've created a text field for the date. Apparently there is no date/datetime data type as such:-
https://www.sqlitetutorial.net/sqlite-date/
Hope this helps,
Phil
I think you need something like this:
MERGE INTO EQUIPMENT A
USING (SELECT * FROM EQUIPMENT B WHERE DATE_COMPLETED IS NULL) C
ON (A.UNITID=C.UNITID)
WHEN MATCHED THEN UPDATE SET A.OVERDUEBY="new value"
WHEN NOT MATCHED THEN INSERT (A.id,A.UnitID,A.Posted_On,A.Date_Completed,A.Note,A.OverDueBy)
VALUES (C.id,C.UnitID,C.Posted_On,NULL,C.Note,C.OverDueBy)
Not sure where new values from update will come from. It's not clear in your question. But something like this could work.

SQL query -> normal english

Can someone please help me put this query in to normal English. No technical terms needed.
This is a past paper question from an exam and it says "Explain in English what the following fragment of code does" Thanks.
CREATE TRIGGER SimpleTrigger
AFTER UPDATE OF Quantity ON Stock
FOR EACH ROW
WHEN (:new.Quantity < 10)
BEGIN
INSERT INTO Reorder(StockCode, StockName, Quantity)
VALUES (StockCode, StockName, 100);
END;
Whenever an update is applied to the column Quantity in the table Stock and the
new value of Quantity is less than 10, I want to insert a new row into the table Reorder.
To set the values for the new row in Reorder copy the values from the columns StockCode and StockName, for the updated row, to their corresponding columns and set the value of Quantity to be 100.
If you are studying for an exam, I suggest you phrase questions
does code XYZ mean Blah Blah Blah.
It will stick better.

Trigger to select data from another table and then update it to the current table SQL Server

Please help me, I have a simple case that makes me little crazy, I have a table called PRODUCTS here is the structure
create table PRODUCT (
ID_PRODUCT CHAR(5) primary key not null,
NAME_PRODUCT CHAR(30),
PRICE_PRODUCT integer
)
then the second table is TRANSACTION, here is the table structure
create table TRANSACTION (
ID_TRANSACTION CHAR(5) primary key not null,
ID_PRODUCT CHAR(30) REFERENCES PRODUCTS(ID_PRODUCT),
PRICE integer,
QUANTITY integer
)
After that I want to create a trigger that works when users fill data into the ID_PRODUCT column in the TRANSACTIONS table, it is automatically trigger will fetch/select data from the column PRICE in the PRODUCTS table according to ID_PRODUCT that has been inserted, then update the column PRICE in table TRANSACTIONS
I'm a starter in SQL Server,... I hope you want to help me in this case, because I have no idea & don't know what I have to do, thanks
Something like this will probably do what you need:
CREATE TRIGGER UpdateTransaction
ON [TRANSACTION]
INSTEAD OF INSERT
AS
BEGIN
INSERT [TRANSACTION] (ID_TRANSACTION, ID_PRODUCT, PRICE, QUANTITY)
SELECT INSERTED.ID_TRANSACTION, INSERTED.ID_PRODUCT, PRODUCT.PRICE_PRODUCT, INSERTED.QUANTITY
FROM INSERTED
JOIN PRODUCT ON INSERTED.ID_PRODUCT = PRODUCT.ID_PRODUCT
END
It'll run INSTEAD of the normal insert and join the row to be INSERTED with the PRODUCT table, allowing it to insert the right PRICE_PRODUCT. You could do something similar for UPDATE's if you need to.
On a side note, TRANSACTION is a terrible name for a table - it's a reserved word in T-SQL (notice that it gets highlighted by your editor, and this website?) - it's part of the language, so I would avoid it in object names.
Perhaps naming your tables like this might be better:
TRANSACTION -> tbl_Transactions
PRODUCT -> tbl_Products
You're having data duplication by having two columns in two tables that are connected (id and price). I suggest removing the TRANSACTION.PRICE column.
You could instead create a VIEW:
CREATE VIEW V_Transaction AS
SELECT
TRANSACTION.ID_TRANSACTION,
TRANSACTION.ID_PRODUCT,
TRANSACTION.QUANTITY * PRODUCT.PRICE_PRODUCT as PRICE,
TRANSACTION.QUANTITY
FROM
TRANSACTION
JOIN ID_PRODUCT ON TRANSACTION.ID_PRODUCT = PRODUCT.ID_PRODUCT
There is just no point (that I see) to store the price per produkt and the price per transaction

how to make an update trigger query in pl sql

I have 2 tables (1) product_warehouse and (2) Return_Vendor Invoice.
i have to update the quantity in product_warehouse by trigger according to the value of Return_Vendor Invoice Table.
where Item_code is unique key in both tables.
For example if the product_warehouse contain 3 quantities , and the shopkeeper returns 1 quantity to vendor then it should be 2 in the Product_warehouse. update query will also acceptable.
create or replace trigger tiuda_return_vendor after insert or update or delete
on return_vendor is
begin
update product_wherehouse
set
quantity = quantity - (:new.quantity - nvl(:old.quantity, 0)
where
item_code = nvl(:new.item_code, :old.item_code);
end;
This trigger works in most cases. You can insert update or delete the return line.
Only thing is: when updating, you cannot update the item_code itself, because the update statement doesn't take that into account. You could easily solve that, but I don't know if it's in your requirements. I usually don't change values like that, but rather remove the item and add a new line for a different item.
Updating the quantity works fine. If you update the quantity, the difference between old and new is calculated and that difference is used to modify the stock quantity in the wherehouse.
create or replace
TRIGGER "WR_RETURN_INVOICE_UPDATE_TRG"
AFTER UPDATE ON RETURN_INVOICE
FOR EACH ROW
BEGIN
UPDATE PRODUCT_WAREHOUSE
SET QUANTITY=QUANTITY-:OLD.QUANTITY
WHERE ITEM_CODE=:OLD.ITEM_CODE;
END WR_RETURN_INVOICE_UPDATE_TRG;
Try this one it will works.