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.
Related
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".
I am using the SQL feature phpMyAdmin to add 1 single record into my table. For simplicity, the record will be blank except for the 'symbol' field.
Table structure:
token_id = auto-increment, primary key
symbol = varchar(255)
every thing else is set to allow null entires, so should be irrelevant
I have tried the following queries, but all result in the same error:
unknown column 'symbol' in 'field list'
What I have tried:
INSERT INTO tokens (symbol) VALUES ('XYZ');
INSERT INTO tokens (symbol) VALUES ("XYZ");
INSERT INTO tokens (symbol) VALUES (XYZ);
INSERT INTO tokens.symbol VALUES ('XYZ');
INSERT INTO `tokens`.`symbol` VALUES ('XYZ');
Any suggestions?
Just for reference, trying the INSERT and using all columns and setting them to null results in the same exact error.
The correct format with backticks is
INSERT INTO tokens (symbol) VALUES ('XYZ');
FYI, I tried your first query on my server and worked fine so might be a problem with table structure.
I can't get a table to accept "" or '' and use the default value. It is inserting NULL instead.
I am trying these commands in the direct input sql window.
INSERT INTO test01 VALUES ("", now(), "");
INSERT INTO test01 VALUES ('', now(), '');
But both just give NULL in the 3rd column. The structure is set to non-null with a default value of "yes". (Without quotation marks).
Here is a screenshot of the structure. You can see NULL is not checked.
http://garryjones.se/extras/so3.png
Default values only work if no value is inserted/updated. If you explicitly set it to an empty string (which is NOT the same as a NULL value) then it will end up with an empty string in the column. Instead of the code above you should eliminate the column from the INSERT statement at all:
INSERT INTO test01 (t1, t2) VALUES ('', now())
Other is already explain the reason here I am adding one more point you are also using current time stamp on update so do not need to use this column as well.
INSERT INTO test01 (t1) VALUES ('')
You could use the DEFAULT keyword: INSERT INTO test01 VALUES ("", now(), DEFAULT);
I am using mysql query browser in that i set not null and default value for one column but while saving it is not considering default value it is showing error as column should not be null. how to solve this please help me
If you explicitly specify the column in an INSERT statement with a value of NULL, defaults are not considered.
For example, in the following query, even if there were a default for column foo, the engine will ignore it and try to insert a NULL:
INSERT INTO myTable(foo, bar) VALUES(NULL, 2);
Either omit the column from your INSERT statement entirely (recommended):
INSERT INTO myTable(bar) VALUES(2);
Or you can use a BEFORE INSERT trigger to catch the NULL value and replace it with what you want.
I am using mysql database.
I have a field user_type in USER table. I would like to restrict the values in this field to be one of ('ADMIN','AGENT','CUSTOMER').
The insert statements should fail if they tried to insert anything else than the above possible values. Also, I need defaulting to 'CUSTOMER' is none is specified in the insert statements.
The possible solution I could think of is use of triggers, but I would like to know How this could be handled more efficiently (possibly in the create table ddl itself?).
Any ideas, How to do this?
This is what the column type "enum" is for. You treat it like a string, and behind the scenes it is stored as an int, and must be one of the values defined in the DDL:
CREATE TABLE users (
id int unsigned NOT NULL auto_increment primary key,
user_type enum('ADMIN', 'AGENT', 'CUSTOMER') NOT NULL default 'CUSTOMER'
)
Then insert like so:
INSERT INTO users (user_type) VALUES ('ADMIN'); // success
INSERT INTO users (user_type) VALUES ('ANONYMOUS'); // failure (or '' if not "strict" mode)
INSERT INTO users (user_type) VALUES (default(user_type)); // uses default
INSERT INTO users () VALUES (); // uses default
INSERT INTO users (user_type) VALUES (NULL); // failure
note
Note that for the query to actually fail, you must use "SQL strict mode". Otherwise, an "empty string" value (which is slightly special in that it has the numeric value of 0) is inserted.
Quoting this docs page:
When this manual refers to “strict mode,” it means a mode where at least one of STRICT_TRANS_TABLES or STRICT_ALL_TABLES is enabled.
I came across this post, and as it dates somewhat back, I was thinking of others coming across it these days too and miss the (in my opinion) simpler approach of simply adding a CHECKconstraint (e.g. this for MySQL, or this for MariaDB).
In my opinion, using a CHECK constraint is much easier than using things like ENUM and / or SET as you don't need to worry about the relations to integer indexes etc. when relying on them. They for example can become weird when you try to preset allowed integer values for a column.
Example, where you want to have a column which has values ranging from 1 to 5:
CREATE TABLE myTable (
myCol INT NOT NULL
CONSTRAINT CHECK (0 < `myCol` < 5)
);