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

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".

Related

How to fix VALUES() deprecation notice in INSERT, ON DUPLICATE KEY UPDATE in MYSQL

VALUES() gives a deprecation notice in mysql 8.0 when used in a query like this
INSERT INTO foo (bar, baz) VALUES (1,2)
ON DUPLICATE KEY UPDATE baz=VALUES(baz)
This post showed how to fix it if you are updating a single row, but not a solution to multiple rows, which is what we use VALUES() for. Mysql 'VALUES function' is deprecated
From that post it says
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 is okay for single insert queries but I don’t know how to scale it for multiple inserts
INSERT INTO foo (bar, baz)
VALUES (1,2) AS new_foo1,
VALUES (3,4) AS new_foo2,
VALUES (5,6) AS new_foo3
ON DUPLICATE KEY UPDATE baz=new_foo.baz #what to do here??
This looks excessive when inserting multiplerowsand not a replacement for the ease of VALUES(). Can someone tell me the correct way to fix the deprecation of VALUES() in this kind of query?
This is in the manual. You write values once, then all the tuples, then as followed by the alias.
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
ON DUPLICATE KEY UPDATE c = new.a+new.b;
This creates one alias to reference each row successively. Similar to a correlation name in JOIN syntax.

MySql INSERT...ON DUPLICATE UPDATE with partial VALUES

I have a list of possibly-incomplete set of values that will be used to append to or update a MySql table using the INSERT...ON DUPLICATE KEY UPDATE construct. The requirements are as follows:
If an operation resolves to an INSERT and the field value IS supplied, use the value supplied;
If an operation resolves to an INSERT and the field value IS NOT supplied, use the field's table DEFAULT value;
If an operation resolves to an UPDATE and the field value IS supplied, use the value supplied;
If an operation resolves to an UPDATE and the field value IS NOT supplied, retain the current (table) field value.
I've come up with the following statement, but the clauses wrapped in ** are erroneous and I'm having difficulty expressing them:
INSERT INTO `test`
(`id`, `num`, `text`)
VALUES
('1', 100, 'aaa'),
('2', 200, DEFAULT),
('3', DEFAULT, 'ccc')
ON DUPLICATE KEY UPDATE
`num` = IF (**VALUES(`num`) = DEFAULT**, `num`, VALUES(`num`)),
`text` = IF (**VALUES(`text`) = DEFAULT**, `text`, VALUES(`text`));
Notes: id is the unique key. Both num and text have default (NOT NULL) values set.
Things I've tried, but aren't satisfactory:
Replacing DEFAULT in VALUES with NULL, and then test for, e.g., IF (VALUES (num) = NULL .... This works, but will insert NULL on INSERT (and generate a warning - e.g., "Column 'text' cannot be null"), which is not acceptable - I need to have the default value applied to the missing fields;
Using something like 'xxx' instead of DEFAULT for missing values, and testing for 'xxx' (STRCMP), but this will insert 'xxx' in case of INSERT;
I've not tried this as I can't find the command/proper syntax, but the idea is to test (in the IF clause) whether num and text in VALUES are literals (num or string) or a MySql keyword (i.e., DEFAULT) - possibly using regex? - and then act accordingly.
Of course, an alternative to the above might entail obtaining existing values from the database and/or hardcoding into the query the default values for the missing fields, but I trust the same result can be achieved more elegantly using a single MySql statement.
Thanks in advance for your feedback.

Slick 3 is generating incorrect MySQL upsert statements for a single column table

I have a very simple MySQL table which represents a set of names using a single String column. I want to use Slick's insertOrUpdate but it is generating incorrect MySQL, causing errors. Specifically, it wants to execute
insert into `TABLE1_NAME` (`column`) values ('value') on duplicate key update
It doesn't specify what to update, so this fails. A similar table with two columns upserts fine, with statements like
insert into `TABLE2_NAME` (`key`, `other_col`) values ('value1', 'value2') on duplicate key update `other_col`=VALUES(`other_col`)
Has anyone seen this? We do set a primary key for TABLE1. We may be doing our table projection mapping incorrectly. We're using Slick 3.1.1.
class Table1(tag: Tag) extends Table[Table1Record](tag, "TABLE1_NAME") {
def value = column[String]("value", O.PrimaryKey, O.Length(254))
def * = (value) <> (Table1Record, Table1Record.unapply)
}
class Table2(tag: Tag) extends Table[Table2Record](tag, "TABLE2_NAME") {
def value1 = column[String]("value1", O.PrimaryKey, O.Length(254))
def value2 = column[String]("value2", O.Length(254))
def * = (value1, value2) <> (Table2Record.tupled, Table2Record.unapply)
}
There's no such concept of "insert or update" into a single column table. The single column is part of the key. If the key matches exactly, then are no other columns to update. If the key didn't match, then the newly inserted row won't be a duplicate of any key, so the update clause won't happen. Because there are no other columns to update, the generated SQL is malformed -- a bit of text has been generated with the assumption that some field names would be appended after it, but there were no field names to append.
By the way, for a table with two columns, the insert statement looks like
insert into `TABLE2_NAME` (`key`, `other_col`)
values ('value1', 'value2')
on duplicate key update `other_col`=VALUES(`other_col`)
It lists only the non-key columns in the update clause. (Getting this correct should help you to better understand what's going on with your single-column table.)

INSERT ON DUPLICATE KEY UPDATE a different value from the insert

I would like to do something like this (the updated value should be different than the inserted value):
"INSERT INTO notification_chat_counts (uid,group_id,count)
VALUES
(",$uid,",",$groupId,",1)
ON DUPLICATE KEY UPDATE
(count = count +1)"
Of course it is possible. Even the MySQL manual has example of inserting different value that the values which would got updated:
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
For your example, the correct query syntax would be:
$query = "INSERT INTO notification_chat_counts (uid,group_id,count)
VALUES (",$uid,",",$groupId,",1)
ON DUPLICATE KEY UPDATE count=count+1"
However for this statement to work you must have an UNIQUE type index defined for this table, so that MySQL can decide if such row already exists in the table or not.
Also remember that inserting values into SQL query like this is dangerous and not recommended. You should use prepared statements for that.

Does SQL Server Offer Anything Like MySQL's ON DUPLICATE KEY UPDATE

In MySQL, if you specify ON DUPLICATE KEY UPDATE and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, an UPDATE of the old row is performed. For example, if column a is declared as UNIQUE and contains the value 1, the following two statements have identical effect:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
I don't believe I've come across anything of the like in T-SQL. Does SQL Server offer anything comparable to MySQL's ON DUPLICATE KEY UPDATE?
I was surprised that none of the answers on this page contained an example of an actual query, so here you go:
A more complex example of inserting data and then handling duplicate
MERGE
INTO MyBigDB.dbo.METER_DATA WITH (HOLDLOCK) AS target
USING (SELECT
77748 AS rtu_id
,'12B096876' AS meter_id
,56112 AS meter_reading
,'20150602 00:20:11' AS time_local) AS source
(rtu_id, meter_id, meter_reading, time_local)
ON (target.rtu_id = source.rtu_id
AND target.time_local = source.time_local)
WHEN MATCHED
THEN UPDATE
SET meter_id = '12B096876'
,meter_reading = 56112
WHEN NOT MATCHED
THEN INSERT (rtu_id, meter_id, meter_reading, time_local)
VALUES (77748, '12B096876', 56112, '20150602 00:20:11');
There's no DUPLICATE KEY UPDATE equivalent, but MERGE and WHEN MATCHED might work for you
Inserting, Updating, and Deleting Data by Using MERGE
You can try the other way around. It does the same thing more or less.
UPDATE tablename
SET field1 = 'Test1',
field2 = 'Test2'
WHERE id = 1
IF ##ROWCOUNT = 0
INSERT INTO tablename
(id,
field1,
field2)
VALUES (1,
'Test1',
'Test2')
SQL Server 2008 has this feature, as part of TSQL.
See documentation on MERGE statement here - http://msdn.microsoft.com/en-us/library/bb510625.aspx
SQL server 2000 onwards has a concept of instead of triggers, which can accomplish the wanted functionality - although there will be a nasty trigger hiding behind the scenes.
Check the section "Insert or update?"
http://msdn.microsoft.com/en-us/library/aa224818(SQL.80).aspx