INSERT INTO … ON DUPLICATE KEY UPDATE …. with condition? - mysql

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.

Related

MYSQL: Using a trigger to make a unix timestamp on INSERT but allow for cloning existing records and avoiding duplicates

I am making an events system.
Let's say an event is "Go dancing" September 12th 2022.
When an event is added to the database we make a unix timestamp on one of the rows.
We use default value unix_timestamp() to do this
This timestamp say 654213987 is used as part of the url so people can sign up to event 654213987.
People can sign up here for example.
http://myevents/signup/654213987
The event organiser writes a description of the event on September 12 2022.
Next year the event organiser wants to do the same event but does not want to rewrite the descriptions. Just duplicate or clone the original without deleting the original.
This would be easy to do programmatically with PHP but I am using XCRUD which I can't modify so my only option is to use triggers or some hard wired part of MYSQL.
When XCRUD makes the duplicate it uses a normal INSERT with a copy of the original minus the primary.
If I make the column UNIQUE it does not allow the clone.
If not it makes a duplicate of the timestamp.
Is it possible to make a trigger (or some other mechanism) which would recognise that there is a duplicate and replace the duplicate with another fresh timestamp?
I have seen on stackoverflow that it is possible to add timestamps with triggers but I just can't figure out how to do this to avoid duplicates.
A sample.
CREATE TABLE test (id INT, ts DATE);
CREATE TRIGGER tr_bi_test_add_1_week
BEFORE INSERT ON test
FOR EACH ROW
BEGIN
WHILE EXISTS ( SELECT NULL
FROM test
WHERE ts = NEW.ts ) DO
SET NEW.ts = NEW.ts + INTERVAL 1 WEEK;
END WHILE;
END
INSERT INTO test VALUES (1, '2022-01-01');
-- inserted as-is
SELECT * FROM test;
id
ts
1
2022-01-01
INSERT INTO test VALUES (2, '2022-01-02');
-- inserted as-is
INSERT INTO test VALUES (3, '2022-01-15');
-- inserted as-is
SELECT * FROM test;
id
ts
1
2022-01-01
2
2022-01-02
3
2022-01-15
INSERT INTO test VALUES (4, '2022-01-01');
-- 2022-01-01 is present => 2022-01-08
SELECT * FROM test;
id
ts
1
2022-01-01
2
2022-01-02
3
2022-01-15
4
2022-01-08
INSERT INTO test VALUES (5, '2022-01-01');
-- 2022-01-01, -08, -15 is present => 2022-01-22
SELECT * FROM test;
id
ts
1
2022-01-01
2
2022-01-02
3
2022-01-15
4
2022-01-08
5
2022-01-22
fiddle

Mysql insert if not available, update if partial available and exit if fully available

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

How to update and insert into db with #transactional on db query

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';

MySql Copy a row, amend value and insert each row

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.

Use ON DUPLICATE KEY UPDATE for some part of unique mapping key

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.