Insert on duplicate sum? - mysql

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;

Related

how to avoid values() with multiple values? [duplicate]

This is my python code which prints the sql query.
def generate_insert_statement(column_names, values_format, table_name, items, insert_template=INSERT_TEMPLATE, ):
return insert_template.format(
column_names=",".join(column_names),
values=",".join(
map(
lambda x: generate_raw_values(values_format, x),
items
)
),
table_name=table_name,
updates_on=create_updates_on_columns(column_names)
)
query = generate_insert_statement(table_name=property['table_name'],
column_names=property['column_names'],
values_format=property['values_format'], items=batch)
print(query) #here
execute_commit(query)
When printing the Mysql query my Django project shows following error in the terminal:
'VALUES function' is deprecated and will be removed in a future release. Please use an alias (INSERT INTO ... VALUES (...) AS alias) and replace VALUES(col) in the ON DUPLICATE KEY UPDATE clause with alias.col instead
Mysql doumentation does not say much about it.What does this mean and how to can i rectify it.
INSERT_TEMPLATE = "INSERT INTO {table_name} ({column_names}) VALUES {values} ON DUPLICATE KEY UPDATE {updates_on};"
Basically, mysql is looking toward removing a longstanding non-standard use of the values function to clear the way for some future work where the SQL standard allows using a VALUES keyword for something very different, and because how the VALUES function works in subqueries or not in a ON DUPLICATE KEY UPDATE clause can be surprising.
You need to add an alias to the VALUES clause and then use that alias instead of the non-standard VALUES function in the ON DUPLICATE KEY UPDATE clause, e.g. change
INSERT INTO foo (bar, baz) VALUES (1,2)
ON DUPLICATE KEY UPDATE baz=VALUES(baz)
to
INSERT INTO foo (bar, baz) VALUES (1,2) AS new_foo
ON DUPLICATE KEY UPDATE baz=new_foo.baz
(This only works on mysql 8+, not on older versions or in any version of mariadb through at least 10.8.3)
Note that this is no different if you are updating multiple rows:
INSERT INTO foo (bar, baz) VALUES (1,2),(3,4),(5,6) AS new_foo
ON DUPLICATE KEY UPDATE baz=new_foo.baz
From https://dev.mysql.com/worklog/task/?id=13325:
According to the SQL standard, VALUES is a table value constructor that returns a table. In MySQL this is true for simple INSERT and REPLACE statements, but MySQL also uses VALUES to refer to values in INSERT ... ON DUPLICATE KEY UPDATE statements. E.g.:
INSERT INTO t(a,b) VALUES (1, 2) ON DUPLICATE KEY
UPDATE a = VALUES (b) + 1;
VALUES (b) refers to the value for b in the table value constructor for the INSERT, in this case 2.
To make the value available in simple arithmetic expressions, it is part of the parser rule for simple_expr. Unfortunately, this also means that VALUES can be used in this way in a lot of other statements, e.g.:
SELECT a FROM t WHERE a=VALUES(a);
In all such statements, VALUES returns NULL, so the above query would not have the intended effect. The only meaningful usage of VALUES as a function, rather than a table value constructor, is in INSERT ... ON DUPLICATE KEY UPDATE. Also, the non-standard use in INSERT ... ON DUPLICATE KEY UPDATE does not extend to subqueries. E.g.:
INSERT INTO t1 VALUES(1,2) ON DUPLICATE KEY
UPDATE a=(SELECT a FROM t2 WHERE b=VALUES(b));
This does not do what the user expects. VALUES(b) will return NULL, even if it is in an INSERT .. ON DUPLICATE KEY UPDATE statement.
The non-standard syntax also makes it harder (impossible?) to implement standard behavior of VALUES as specified in feature F641 "Row and table constructors".

MySQL: how to update column using value before change

There is a table with three column: id, field1, field2.
And there is a row: id=1, field1=1, field2=1.
Run a update SQL: UPDATE my_table SET field1=field2+1, field2=field1+1 WHERE id=1;
I expected the result is: id=1, field1=2, field2=2. But in fact I got: id=1, field1=2, field2=3. Because when calculating field2=field1+1, the value of field1 has changed!
I figure out a SQL to solve this problem:
UPDATE my_table dest, (SELECT * FROM my_table) src
SET dest.field1=src.field2+1, dest.field2=src.field1+1
WHERE dest.id=1;
However I want to insert a record, and if the row was existed then do a update just like above.
INSERT INTO my_table (id, field1, field2) VALUES(1, 1, 1)
ON DUPLICATE KEY UPDATE
field1=field2+1, field2=field1+1;
This SQL has problem same as the first SQL. So how can I do this update using the value before change with ON DUPLICATE KEY UPDATE clause?
Thanks for any help!
Couldn't think of anything else but a temp variable. However, couldn't think of a way to make SQL syntax work, other than this:
set #temp = 0;
update test.test set
f1 = (#temp:=f1),
f1 = f2 + 1,
f2 = #temp + 1
where id = 1;
Hope this helps, and hope even more it helps you find a better way :)
I find a trick way to do this.
Use the IF clause to create temp variable. Field update use temp variable to calculate.
INSERT INTO my_table (id, f1, f2) VALUES(1, 1, 1)
ON DUPLICATE KEY UPDATE
id=IF((#t1:=f1 & #t2:=f2), 1, 1), f1=#t2+1, f2=#t1+1;
There is some point to notice:
The performance is a bit slow. Especially copy TEXT value to temp variable.
If field id need to use IF clause, the expr will be more complicated like:
((#t1:=f1 & #t2:=f2) || TRUE) AND (Your Condition)

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.

ON DUPLICATE KEY UPDATE - add to existing value

I have SQL (MySQL 5.x) query like:
INSERT INTO table (val1),
ON DUPLICATE KEY UPDATE `val1` = VALUES(`val1`)
And this works fine.
Now i need to update it with a sum of VALUES(val1) + ruby variable.
INSERT INTO table (val1),
ON DUPLICATE KEY UPDATE `val1` = VALUES(`val1`) + #{ruby_variable}
throws me an error.
(Ruby here is just an example, actually i need to sum VALUES(val1) + integer)
How it could be done?
Right at the top of the fine manual you will see an example of exactly the sort of thing you're trying to do:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
So you're looking for this:
connection.execute(%Q{
INSERT INTO table (val1) VALUES(#{connection.quote(x)})
ON DUPLICATE KEY UPDATE `val1` = `val1` + #{connection.quote(ruby_variable)}
})
Where x is what you're trying to insert and ruby_variable is what you want to add to val1 when there is a duplicate. You need a VALUES for the INSERT, not for the ON DUPLICATE.

finding value of column after ON DUPLICATE KEY UPDATE

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*/) ;