I have one table warehouse_item_mapping
having columns - warehouseId, itemId, stockQuantity
warehouse_id item_id stock_quantity
1 123 10
1 234 20
1 345 30
This table is being used as inventory management as maintaining current stock of items in warehouse.
Now if new stock has come with some items including new items with current stock quantity.
Like
1 123 50
1 234 50
1 678 50 (new item)
I have to update the stock quantity in table as sum of existing quantity and current stock quantity. And if mapping not found in table then insert that new mapping.
For this case table should be updated as
warehouse_id item_id stock_quantity
1 123 60
1 234 70
1 345 30
1 678 50
How to do this in single query.
Follow up question in this is
If someone used the current stock from table in between get and update query then how to maintain lock or transactional for whole flow.
INSERT ON DUPLICATE KEY UPDATE is what you need. In combination with pre selected stock_quantity. You need to make item_id PRIMARY KEY.
Now of course you will be using one SELECT and one INSERT INTO statement with PHP ( i guess) variables.
SELECT #stock_quantity:=stock_quantity FROM warehouse_item_mapping WHERE item_id=123;
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES (1, 123, 50)
ON DUPLICATE KEY UPDATE warehouse_id=1, stock_quantity=#stock_quantity+50;
Working example: db-fiddle
RESULT:
Results
Query #1 Execution time: 0ms
warehouse_id item_id stock_quantity
1 123 60
1 234 70
1 345 30
1 678 50
SO lats say you are using PHP:
PHP:
'$item_id' // your new OR old inserting item id.
'$warehouse_id'
'$stock_quantity' // Your NEW stock number.
START TRANSACTION;
SELECT #stock_quantity:=stock_quantity
FROM warehouse_item_mapping WHERE item_id='$item_id';
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES ('$warehouse_id', '$item_id', '$stock_quantity')
ON DUPLICATE KEY UPDATE warehouse_id='$warehouse_id', stock_quantity=#stock_quantity+'$stock_quantity';
COMMIT;
This will try to insert a value only if PRIMARY KEY item-id doesn't exists. If it does it will update other two fields.
EDIT:
I think you are confused what #transaction is, read about it here.
Above is example of one as you asked for it but this can also be done in one query as mentioned in comment. Example:
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES (1, 123, 50) ON DUPLICATE KEY UPDATE
warehouse_id=1, stock_quantity=stock_quantity+50;
Working Fiddle.
PHP:
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES ('$warehouse_id', '$item_id', '$stock_quantity') ON DUPLICATE KEY
UPDATE warehouse_id='$warehouse_id', stock_quantity=stock_quantity+'$stock_quantity';
Related
I am still wondering if there is something like a conditional on duplicate update in MySQL 5.7
I have a table which is updated by different sources.
Let’s assume I have a table
CREATE TABLE t
(
name VARCHAR(100),
value INT,
last update DATETIME
)
I have 3 rows
name
value
lastupdate
a
10
2021-01-01
b
20
2021-02-01
c
30
2021-03-01
Now I have some data to be imported
name
value
lastupdate
a
20
2021-01-01
b
40
2021-01-01
c
60
2021-04-01
The result of the query should be
name
value
lastupdate
a
20
2021-01-01
b
20
2021-02-01
c
60
2021-03-01
Can this be done by one insert query or must I check first if the last update of the existing data in the table is newer then the date of the import data?
Assuming that name is the PRIMARY KEY of the table or is defined as UNIQUE, you can use a CASE expression:
INSERT INTO t (name, value, lastupdate) VALUES
('a', 20, '2021-01-01'),
('b', 40, '2021-01-01'),
('c', 60, '2021-04-01')
ON DUPLICATE KEY UPDATE
value = CASE WHEN VALUES(lastupdate) >= lastupdate THEN VALUES(value) ELSE value END;
See the demo.
Note that (from INSERT ... ON DUPLICATE KEY UPDATE Statement):
The use of VALUES() to refer to the new row and columns is deprecated
beginning with MySQL 8.0.20, and is subject to removal in a future
version of MySQL. Instead, use row and column aliases, as described in
the next few paragraphs of this section.
So, if your version of MySql is 8.0.20+ it is recommended to use an alias instead of VALUES():
INSERT INTO t (name, value, lastupdate) VALUES
('a', 20, '2021-01-01'),
('b', 40, '2021-01-01'),
('c', 60, '2021-04-01') AS new
ON DUPLICATE KEY UPDATE
t.value = CASE WHEN new.lastupdate >= t.lastupdate THEN new.value ELSE t.value END;
See the demo.
i need to take the appropriate action depending on whether the values are found, partially found or not available at all.
This is my table:
id. emp_code. ot_date. ot_hours
1 123 2021-05-01 3
2 567 2021-05-01 1
Now i have these 3 data:
Data #1
emp_code: 123
ot_date: 2021-05-01
ot_hours: 3
Data #2
emp_code: 123
ot_date: 2021-05-02
ot_hours: 3
Data #3
emp_code: 567
ot_date: 2021-05-01
ot_hours: 2.5
This is my logic to decide and take action:
select emp_code, ot_date where values are (emp_code, ot_date)
from ot_forecast
if it exist
select emp_code, ot_dat,ot_hours where values are (emp_code, ot_date, ot_hours)
from ot_forecast
if exist then exit
else update values (emp_code, ot_date, ot_hours)
else insert values (emp_code, ot_date, ot_hours)
Based on my logic, data 1 will be skipped, data 2 will be inserted and data 3 will be updated
Problem is, I can't figure out how to put this in codes. I'm already stuck at the first line.
Hope someone can guide me with this.
Thanks
If you create emp_code,ot_date as a unique (or primary (id seems pointless)) key, that enables INSERT ON DUPLICATE KEY UPDATE.
CREATE UNIQUE INDEX ot_forecast ( emp_code, ot_date)
Your query will become:
INSERT INTO ot_forecast
SET emp_code = xxx, ot_date= yyyy, ot_hours = zzzz
ON DUPLICATE KEY UPDATE ot_hours = zzzz
The first case of yours as the select will incur an update and maybe this is ok as its effectively a no-op.
If you where using MariaDB you could also use INSERT RETURNING:
INSERT INTO ot_forecast
SET emp_code = xxx, ot_date= yyyy, ot_hours = zzzz
ON DUPLICATE KEY UPDATE ot_hours = zzzz
RETURNING emp_code, ot_date, ot_hours
I have a table with 7 fields - one, product_special_id, being AUTO_INCREMENT.
The table contains the product prices for different product groups.
So for example:
product_special_id: 1532 (AUTO_INCREMENT)
product_id: 4
customer_group_id: 3
priority: 0
price: 280.5000
date_start: 0000-00-00
date_end: 0000-00-00
I need to copy each record assigned to customer_group_id '3' to a new record in the same table with a new customer_group_id - lets say '5'.
The product_special_id for the new record must be unique. The remaining five fields remain the same.
The original record needs remain unaltered.
Can this be done?
Thanks
Just use insert . . . select:
insert into t(product_id, customer_group_id, priority, price, date_start, date_end)
select product_id, 5, priority, price, date_start, date_end
from t
where customer_group_id = 3;
This query work for your issue:
insert into `tablename` (product_id,customer_group_id,priority,price,date_start,date_end)
select product_id,5,priority,price,date_start,date_end from `tablename` where customer_group_id=3
For more information about insert select syntax see the mysql documentation.
I have table as shown below
ItemAttMapID ItemAttributeID VendorID vItemID Qty
1 4 1 2 60
2 4 2 3 10
3 3 5 5 20
In this table ItemAttMapID is auto_increment primary_key
and ItemAttributeID, VendorID, VItemID is Unique Mapping.
I used following query
INSERT INTO `client`.`mapping`
(`ItemAttMapID`, `ItemAttributeID`, `VendorID`, `vItemID`, `Qty`)
VALUES
(NULL, 4, '2', '3', '70'),
(NULL, 3, '5', '5', '80')
ON DUPLICATE KEY UPDATE Qty = VALUES(Qty);
It gives me proper result which I want
but when I used following query instead of updating the perticular record it inserts the new one.
INSERT INTO `client`.`mapping`
(`ItemAttMapID`, `ItemAttributeID`, `VendorID`, `vItemID`, `Qty`)
VALUES
(NULL, 2, '5', '5', '80')
ON DUPLICATE KEY UPDATE Qty = VALUES(Qty);
and change in table is like this
ItemAttMapID ItemAttributeID VendorID vItemID Qty
1 4 1 2 60
2 4 2 3 10
3 3 5 5 20
4 2 5 5 80
I want the 3rd number row to update but It is inserting a new record to table.
How can I achieve this using ON DUPLICATE KEY UPDATE.
EDIT:
I want to check only, if VendorID and vItemID is already present then update the record ( NOT THE ItemAttributeID) using ON DUPLICATE KEY UPDATE..
Because if I put ItemAttributeID same along with VendorID and vItemID in query then it updates the record otherwise its inserting new record.
I have a table where different participants are given multiple boxes of medicines on multiple days. I am trying to create a frequency table showing how much medicines have been distributed by the number of boxes to the participants.
The result I'm looking for is -
2 boxes = 1 (since only Lynda got a total of 2 boxes), 4 boxes = 2 (since Ryan and Rinky both got a total of 4 boxes after adding up the medicine boxes)
Please let me know what approach would be the best in this case.
Thanks for your help.
-Nams
I think you want:
SELECT t.SumOf, Count(t.[PARTICIPANT ID]) AS CountOf
FROM (SELECT Table1.[PARTICIPANT ID], Sum(Table1.MEDICINE_BOX) AS SumOf
FROM Table1
GROUP BY Table1.[PARTICIPANT ID]) AS t
GROUP BY t.SumOf;
Where table1 is the name of your table.
If your table is like this:
medicine_dispense
participantID date amount_boxes
ABC 8/29/12 1
ABC 8/30/12 2
XYZ 8/29/12 1
XYZ 8/30/12 1
then a query like this:
select
amount_boxes, count(participantID)
from
medicine_dispense
should work
I'll use generic SQL. You can paste SQL into Access queries in SQL view. (You might have to delete the CHECK() constraint.)
create table participant_meds (
participant varchar(10) not null,
distribution_date date not null default current_date,
num_boxes integer not null check (num_boxes > 0),
primary key (participant, distribution_date)
);
insert into participant_meds values ('Ryan', '2012-02-03', 1);
insert into participant_meds values ('Ryan', '2012-06-07', 3);
insert into participant_meds values ('Rinky', '2012-02-28', 4);
insert into participant_meds values ('Lynda', '2012-03-04', 2);
insert into participant_meds values ('Russ', '2012-04-05', 2);
insert into participant_meds values ('Russ', '2012-05-08', 2);
insert into participant_meds values ('Russ', '2012-06-12', 2);
Resulting data, sorted, for copy/paste.
participant distribution_date num_boxes
Lynda 2012-03-04 2
Rinky 2012-02-28 4
Russ 2012-04-05 2
Russ 2012-05-08 2
Russ 2012-06-12 2
Ryan 2012-02-03 1
Ryan 2012-06-07 3
This query gives you the total boxes per participant.
select sum(num_boxes) boxes, participant
from participant_meds
group by participant;
6;"Russ"
2;"Lynda"
4;"Ryan"
4;"Rinky"
Use that query in the FROM clause as if it were a table. (I'd consider storing that query as a view, because I suspect that the total number of boxes per participant might be useful. Also, Access has historically been good at optimizing queries that use views.)
select boxes num_boxes, count(participant) num_participants
from (select sum(num_boxes) boxes, participant
from participant_meds
group by participant) total_boxes
group by num_boxes
order by num_boxes;
num_boxes num_participants
--
2 1
4 2
6 1