I've learned this along the way but can't figure out where I read it or heard it, as there is nothing I have found online supporting it, but I remember that when upgrading from mysql4.x to mysql5.x, one of the required changes was that the auto-increment field for inserts had to change from '' to NULL if it was included.
I know its not required to have in the insert anyway, but just for point of interest...
Mysql 4.x would allow:
INSERT INTO TABLE (table_id, name, location) VALUES ('', 'john', 'NY');
But mysql 5.x had to have:
INSERT INTO TABLE (table_id, name, location) VALUES (NULL, 'john', 'NY');
I can't find any information on mysql's site to support this, but I know for a fact it throws an error in mysql 5.x and know it worked with '' in 4.x, but where is this documented?
Both the 4.1 and 5.0 docs state that 0 or NULL is required:
No value was specified for the
AUTO_INCREMENT column, so MySQL
assigned sequence numbers
automatically. You can also explicitly
assign NULL or 0 to the column to
generate sequence numbers.
It does not matter, mysql internally still convert to integer
mysql> CREATE TABLE some_test ( id int(10) unsigned NOT NULL auto_increment, primary key(id));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into some_test values ('');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+------------------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------------------+
| Warning | 1264 | Out of range value adjusted for column 'id' at row 1 |
+---------+------+------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from some_test;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
However, I will suggest use 0 to avoid this warning
Related
I have a mysql table which has a data structure as follows,
create table data(
....
name char(40) NULL,
...
)
But I could insert names which has characters more than 40 in to name field. Can someone explain what is the actual meaning of char(40)?
You cannot insert a string of more than 40 characters in a column defined with the type CHAR(40).
If you run MySQL in strict mode, you will get an error if you try to insert a longer string.
mysql> create table mytable ( c char(40) );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into mytable (c) values ('Now is the time for all good men to come to the aid of their country.');
ERROR 1406 (22001): Data too long for column 'c' at row 1
If you run MySQL in non-strict mode, the insert will succeed, but only the first 40 characters of your string is stored in the column. The characters beyond 40 are lost, and you get no error.
mysql> set sql_mode='';
Query OK, 0 rows affected (0.00 sec)
mysql> insert into mytable (c) values ('Now is the time for all good men to come to the aid of their country.');
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> show warnings;
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1265 | Data truncated for column 'c' at row 1 |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select c from mytable;
+------------------------------------------+
| c |
+------------------------------------------+
| Now is the time for all good men to come |
+------------------------------------------+
1 row in set (0.00 sec)
I recommend operating MySQL in strict mode (strict mode is the default since MySQL 5.7). I would prefer to get an error instead of losing data.
For STRICT_ALL_TABLES, MySQL returns an error and ignores the rest of the rows. However, because the earlier rows have been inserted or updated, the result is a partial update. To avoid this, use single-row statements, which can be aborted without changing the table.
For STRICT_TRANS_TABLES, MySQL converts an invalid value to the closest valid value for the column and inserts the adjusted value. If a value is missing, MySQL inserts the implicit default value for the column data type. In either case, MySQL generates a warning rather than an error and continues processing the statement. Implicit defaults are described in Section 11.6, “Data Type Default Values”.
I tried to set STRICT_TRANS_TABLE and tried inserting a invalid record without a transaction. Despite as documented it showed a error.
mysql> show create table mydemo;
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| mydemo | CREATE TABLE `mydemo` (
`pk` int(20) NOT NULL,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM mydemo;
+----+------+
| pk | name |
+----+------+
| 1 | Test |
+----+------+
1 row in set (0.00 sec)
mysql> SELECT ##SESSION.sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ##SESSION.sql_mode |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into mydemo values (2, "abcdefghijklmn");
ERROR 1406 (22001): Data too long for column 'name' at row 1
mysql> select ##SESSION.autocommit;
+----------------------+
| ##SESSION.autocommit |
+----------------------+
| 1 |
+----------------------+
1 row in set (0.00 sec)
What do they mean by Non transaction table ?
In the above, why do it shows error instead of warning ?
What if both the modes are set down ?
Your table type is innodb, which is a transactional table type (supports transactions), therefore te paragraph before the two you quoted applies:
For transactional tables, an error occurs for invalid or missing values in a data-change statement when either STRICT_ALL_TABLES or STRICT_TRANS_TABLES is enabled. The statement is aborted and rolled back.
The two paragraphs you quoted applies to non-transactional tables, such as myisam.
I have a MySQL table with a JSON column. I want to update some rows in the JSON column to change a json value from a float to an integer. e.g {"a": 20.0} should become {"a": 20}. It looks like MySQL finds these 2 values equivalent, so it never bothers to update the row.
Here is the state of my test table:
mysql> describe test;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| val | json | YES | | NULL | |
+-------+------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> select * from test;
+----+-------------+
| id | val |
+----+-------------+
| 1 | {"a": 20.0} |
+----+-------------+
1 row in set (0.00 sec)
My aim is to change val to {"a": 20}
I've tried the following queries:
mysql> update test set val=JSON_OBJECT("a", 20) where id=1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
(0 rows changed)
mysql> update test
set val=JSON_SET(
val,
"$.a",
FLOOR(
JSON_EXTRACT(val, "$.a")
)
)
where id=1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
(0 rows changed)
mysql> insert into test (id, val) values (1, JSON_OBJECT("a", 20)) ON DUPLICATE KEY UPDATE id=VALUES(id), val=VALUES(val);
Query OK, 0 rows affected, 2 warnings (0.00 sec)
(0 rows affected)
It looks like it doesn't matter how I try to write it, whether I attempt to modify the existing value, or specify a whole new JSON_OBJECT. So I'm wondering if the reason is simply that MySQL considers the before & after values to be equivalent.
Is there any way around this?
(This does not address the original Question, but addresses a problem encountered in Answering it.)
Gross... 8.0 has a naughty history of all-too-quickly removing something after recently deprecating it. Beware. Here is the issue with VALUES from the Changelog for 8.0.20:
----- 2020-04-27 8.0.20 General Availability -- -- -----
The use of VALUES() to access new row values in INSERT ... ON DUPLICATE KEY UPDATE statements is now deprecated, and is subject to removal in a future MySQL release. Instead, you should use aliases for the new row and its columns as implemented in MySQL 8.0.19 and later.
For example, the statement shown here uses VALUES() to access new row values:
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
Henceforth, you should instead use a statement similar to the following, which uses an alias for the new row:
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
ON DUPLICATE KEY UPDATE c = new.a+new.b;
Alternatively, you can employ aliases for both the new row and each of its columns, as shown here:
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p)
ON DUPLICATE KEY UPDATE c = m+n;
For more information and examples, see INSERT ... ON DUPLICATE KEY UPDATE Statement.
I am doing similar INSERT...SELECT query to this
INSERT INTO table (value1, value2)
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM table
WHERE value1='stuff for value1' AND value2='stuff for value2')
LIMIT 1
, where table has auto-genrated id.
And I would like to know if it was inserted or not, of course. I assume the way to do that is to use mysql_insert_id(). It returns 0 if no insertions happen and 1 if insertions happen. Looking more details here.
If an INSERT ... SELECT statement is executed, and NO automatically
generated value is successfully inserted, mysql_insert_id() RETURNS
the ID of the last inserted row.
What does it return if no auto-generated ID was successfully inserted? Is this a doc typo?
UPDATE1
So far I did testing in C and mysql_insert_id() returns always 0 if insertion did not happen even if the last insertion succeeded and mysql_insert_id() returned non-zero result. A paragraphs in the same manual, mentioned above, confirms this behavior by saying:
mysql_insert_id() returns 0 if the previous statement does not use an AUTO_INCREMENT value. ....
The value of mysql_insert_id() is affected only by statements issued within the current client connection. It is not affected by statements issued by other clients.
The LAST_INSERT_ID() SQL function will contain the value of the first automatically generated value that was successfully inserted. LAST_INSERT_ID() is not reset between statements because the value of that function is maintained in the server. ....
And that feels kind of logical otherwise INSERT...SELECT would be useless in many cases, if you cannot know within the code if your insertion worked or not. But it totally contradicts to the statement above. Did anyone have experience with this?
UPDATE2
From MariaDB manual, also suggests that the value should be zero in case of insertion did not happen:
The mysql_insert_id() function returns the ID generated by a query on
a table with a column having the AUTO_INCREMENT attribute or the value
for the last usage of LAST_INSERT_ID(expr). If the last query wasn't
an INSERT or UPDATE statement or if the modified table does not have a
column with the AUTO_INCREMENT attribute and LAST_INSERT_ID was not
used, this function will return zero.
The wording could be more clear, but what it means is that if your INSERT causes an error, mysql_insert_id() (or the SQL function last_insert_id()) continues to report whatever it did based on an earlier successful INSERT.
Here's a demo:
mysql> create table foo( id int auto_increment primary key);
mysql> create table bar( id int primary key);
mysql> insert into bar (id) values (1), (2), (10);
mysql> insert into foo select id from bar;
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 0 |
+------------------+
No new auto-inc values were generated, because my INSERT gave specific values to insert.
Let's generate some new values:
mysql> insert into foo select null from bar;
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 11 |
+------------------+
This is expected, because last_insert_id() will report the first id generated by a batch insert. You have to do the math to figure out how many rows were inserted, so you can know the rest of the id's. The id's generated in this way are guaranteed to be unique and consecutive.
Now let's try inserting some duplicates, which will cause an error:
mysql> insert into foo select id from bar;
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
Now comes the point of the sentence in the documentation: there has been no change in what last_insert_id() reports.
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 11 |
+------------------+
Likewise, even if the INSERTs are successful, but do not cause any new auto-inc values to be generated, there is no change in what last_insert_id() reports.
mysql> insert into foo select id+20 from bar;
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 11 |
+------------------+
Many people assume last_insert_id() reports the most recent primary key value inserted, but it doesn't. It only reports values that were generated automatically by the auto-inc feature.
mysql_affected_rows is your friend. It will be greater than 0, if you successfully inserted rows (except when it returns (my_ulonglong)-1, which indicates failure). In your case, since you insert at most 1 row, you just need to check whether it returned 1.
It looks like it will return the id that was last auto-generated:
MariaDB [stackoverflow]> desc a;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| a | varchar(20) | YES | | NULL | |
| b | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
MariaDB [stackoverflow]> insert into a(a,b) values('haha', 'haha');
Query OK, 1 row affected (0.03 sec)
MariaDB [stackoverflow]> select LAST_INSERT_ID() from dual;
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 1 |
+------------------+
MariaDB [stackoverflow]> insert into a(a,b) select 'hi', 'hello' from dual;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
MariaDB [stackoverflow]> select LAST_INSERT_ID() from dual;
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
MariaDB [stackoverflow]> insert into a(a,b) select 'hi', 'hello' from dual where not exists (select * from a where a='hi' and b='hello') limit 1;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [stackoverflow]> select LAST_INSERT_ID() from dual;
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
I have a MySQL Database 5.6.32 connected to SuiteCRM. I am using a plugin that allows for the creation of an auto increment field without any coding.
The challenge I'm having is that I have created this auto increment field after records with data are already in our system. I need to update all previous entries with the auto increment values.
When I create a new record the auto increment field works fine, but I need the unique number for all records as this is being used as a Unique Identifier and the default ID in the system is too long for us to use.
The type of auto increment field it created in the MySQL database is as follows:
# Name Type Collation Null Default
10 customer_number_c varchar(80) utf8_general_ci Yes NULL
This is what I have tried so far to try and populate the field:
UPDATE `suitecrm`.`accounts_cstm` SET `customer_number_c` = auto_increment
The result is:
ERROR #1054 - Unknown column 'AUTO_INCNREMENTAL' in 'Field list'
The field already has a default value of NULL in it as well.
MySQL's builtin auto-increment feature only works with columns of integer data types. Yours is varchar(80). I'm not sure why you did that, but I assume it was deliberate.
You could use a session variable to populate the customer number. As you assign values, it will implicitly cast the integer value of the session variable to the string representation.
SET #num := 0;
UPDATE suitecrm.accounts_cstm
SET customer_number_c = (#num := #num + 1)
ORDER BY ...;
You would have to specify some ORDER BY clause to make sure the increasing values get assigned in the order you want them to be.
But you still won't be able to use AUTO_INCREMENT on the customer_number_c column. So your app must generate new customer number values before inserting new rows to this table.
MySQL will retroactively populate existing rows for you if you add an auto_increment primary key. I just validated this with the following test code:
mysql> create table mytable (name varchar(32)) engine=innodb;
Query OK, 0 rows affected (0.04 sec)
mysql> insert into mytable (name) values ('miles'), ('trane'), ('monk');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from mytable;
+-------+
| name |
+-------+
| miles |
| trane |
| monk |
+-------+
3 rows in set (0.00 sec)
mysql> alter table mytable add column id int unsigned primary key auto_increment first;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from mytable;
+----+-------+
| id | name |
+----+-------+
| 1 | miles |
| 2 | trane |
| 3 | monk |
+----+-------+
3 rows in set (0.00 sec)