finding value of column after ON DUPLICATE KEY UPDATE - mysql

Consider this statement:
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE d=d+1;
I need the value of d.
Is it possible to obtain it without performing a further SELECT?
There is a unique index on a,b,c. Would this index be used for better performance? This table will have a large number of rows.

Assuming you will be running both queries using the same connection, You can use the LAST_INSERT_ID(expr) function to set the current value of d, and check the value of LAST_INSERT_ID() together with the ROW_COUNT() function to find out if the record was inserted or updated:
INSERT INTO table (a,b,c)
VALUES (1,2,3)
ON DUPLICATE KEY UPDATE d = LAST_INSERT_ID(d + 1);
SELECT IF(ROW_COUNT() = 2, LAST_INSERT_ID(), 0 /*default value*/) ;
You can also use session variables:
INSERT INTO table (a,b,c)
VALUES (1,2,3)
ON DUPLICATE KEY UPDATE d = #tmp := (d + 1);
SELECT IF(ROW_COUNT() = 2, #tmp, 0 /*default value*/) ;

Related

MySQL insert ignore, on duplicate key update, with condition

I want to insert or update a record in a table. If it doesn't exist, it should be inserted. If it exists, then I only want to update the record if a certain condition is met. Is there a way to do this using a single INSERT statement? Something like this:
CREATE TABLE test1 SELECT 1 id, now() dt;
ALTER TABLE test1 ADD PRIMARY KEY (id);
INSERT IGNORE INTO test1 (id, dt) VALUES
(1, '2023-02-06 13:00:00')
ON DUPLICATE KEY UPDATE dt = VALUES(dt) WHERE dt = somedatetime;
-- i.e. always insert, but only update dt if existing dt value is something specific
I know I can do this using a transaction, I'm just wondering if something like this can be done in a single statement.
I was trying things out while writing the question and I found this to be one solution:
INSERT IGNORE INTO test1 (id, dt)
SELECT 1, '2023-02-06 13:00:00'
FROM test1
WHERE (NOT EXISTS(SELECT * FROM test1 WHERE id = 1))
OR (id = 1 AND dt = somedatetime)
ON DUPLICATE KEY UPDATE dt = VALUES(dt);

Insert on duplicate sum?

Say a table exists,
name
val
John
1
Mark
2
Adam
3
Is there anyway to write an upsert statement like
INSERT INTO table (name, val) VALUES (x, y) ON DUPLICATE KEY UPDATE val = currentValueInTable + y;
There is no problem referring to the current value in the ON DUPLICATE UPDATE clause. If you are trying to make it automatically get the value from the VALUES clause to add, then there is an old way to do it using the values function:
INSERT INTO `table` (name, val) VALUES ('Mark',4),('Sally',5)
ON DUPLICATE KEY UPDATE val = val + values(val);
This is supported through version 5.7. In version 8, it gives a deprecation warning, but still works. In version 8, the preferred way is to give the VALUES clause an alias and refer to the value using that:
INSERT INTO `table` (name, val) VALUES ('Sally',6),('Jane',7) AS newvalues
ON DUPLICATE KEY UPDATE val = table.val + newvalues.val;
fiddle
Note that the deprecated values function, because of how it was implemented, can unfortunately be used outside of an ON DUPLICATE KEY UPDATE clause, and returns just returns null:
select a from t where a=values(a)
Even more unfortunately, it still returns null if used in a subquery in an ON DUPLICATE KEY UPDATE clause, making queries like this not work as intended:
INSERT INTO t1 VALUES(1,2) ON DUPLICATE KEY
UPDATE a=(SELECT a FROM t2 WHERE b=VALUES(b));
Just use column name:
INSERT INTO table (name, val) VALUES (x, y) ON DUPLICATE KEY UPDATE val = val + y;

Update if tuple is in the set, else insert in MySQL

I ran some query and got a set of tuples like ((A1,B1), (A2,B2), (A3,B3)....).
I need to check if a tuple from above set exists in a table XYZ(A,B,C). If it exists, then update C else insert (Ax,Bx,C) into the table.
I tried using the below query but it doesn't work. Is there any other way?
CASE WHEN EXISTS (SELECT * from XYZ as t where (t.A, t.B) in (select u.A, u.B from diff_table as u)) THEN
THEN UPDATE XYZ as v SET C = 1 WHERE (v.A, v.B) = (t.A, t.B) ELSE
INSERT INTO XYZ (A,B,C) values (u.A, u.B, 1) END;
I'd do it the other way round:
Update the ones that exist
Insert the ones that don't
Or if you have primary keys
Insert all with default value for C (existing will get rejected)
Update all
The functionality you want is insert . . . on duplicate key insert.
First, you need an appropriate index (if you don't have one):
create index idx_xyz_a_b on xyz(a, b)
Then:
insert into xyz(a, b, c)
select dt.a, dt.b, 1
from diff_table dt
on duplicate key update set c = 1;
By the way, if the value already exists, are you sure you want to set it to 1 instead of incrementing it?
insert into xyz(a, b, c)
select dt.a, dt.b, 1
from diff_table dt
on duplicate key update set c = c + 1;

MYSQL ON DUPLICATE KEY UPDATE and IGNORE in one query

Ik execute a query that inserts some values in de table, if a combination of ID, Year, rownum (unique index) exists that i do a regular ON DUPLICATE KEY UPDATE and so the row is updated. The query looks like this:
INSERT INTO data_advertenties_prijzen (`ID`, `year`, `rownum`, `status_prijs`,
`datum_dag`, `timestamp_dag`)
VALUES (100,2014,1,1,'2014-01-01',1388534400),
(100,2014,2,1,'2014-07-16',1405468800),
(100,2014,3,1,'2014-07-26',1406332800)
ON DUPLICATE KEY UPDATE
status_prijs = VALUES(status_prijs),
datum_dag = VALUES(datum_dag),
timestamp_dag = VALUES(timestamp_dag)
Nothing difficults there, but….
I also want to do a ON DUPLICATE IGNORE for 1 value in the same query. I Also want to insert one row for 2015. For example: (100,2015,1,1,'2015-01-01',1405468800)…
If there is already a row with ID=100, Year=2015 And rownum=1 the insert of that row must be ignored.
How to do that?
You could change the values conditionally in the ON DUPLICATE clause.
I did this experiment to make sure it works:
INSERT INTO data_advertenties_prijzen VALUES (100, 2014, 1, 7, now(), 1406332800)
ON DUPLICATE KEY UPDATE
status_prijs = IF((id,year,rownum)=(100,2015,1), status_prijs, VALUES(status_prijs)),
datum_dag = IF((id,year,rownum)=(100,2015,1), datum_dag, VALUES(datum_dag)),
timestamp_dag = IF((id,year,rownum)=(100,2015,1), timestamp_dag, VALUES(timestamp_dag));
So if I try to insert a specific trio of id/year/rownum, it just uses the existing value, else if it's some other id/year/rownum, it uses the VALUES I specify in the INSERT.
Unfortunately, you must repeat the expression for each column you want to update.

MYSQL Procedures - INSERT INTO Return value

I have the MYSQL stored procedure defined as shown below:
DROP PROCEDURE IF EXISTS TEST_LOCATION;
CREATE PROCEDURE `TEST_LOCATION`()
BEGIN
DECLARE VCount tinyint(1) DEFAULT 1;
INSERT INTO my_counts_model (type,code,model_code,count)
VALUES ( 1,1,456,1)
ON DUPLICATE KEY UPDATE count = count + 1;
END;
The procedure updates the count if the type,code,model_code has the value in table, otherwise inserts a new record. I mean the key is on type,code,model_code.
I want to get whether it is a new insert or the count update for further operations. I am trying to check for the return value of the INSERT INTO, but couldn't find syntax or any solution.
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_row-count
"ROW_COUNT(): For INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows value is 1 if the row is inserted as a new row and 2 if an existing row is updated."
You also can do something like this -
add new 'flag' field to the table and set 1 on INSERT or 2 on UPDATE, e.g.
INSERT INTO my_counts_model (type, code, model_code, count, flag)
VALUES ( 1, 1, 456, 1, 1)
ON DUPLICATE KEY UPDATE count = count + 1, flag = 2;
In this case the information will be stored in the table.