MySQL/MariaDB unable to handle unique keys with when using utf8mb4 - mysql

We have MySQL table with utf8mb4 strings:
CREATE TABLE `test` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`code` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `test_code_unique` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
When inserting special characters there appears to be wrong conversion:
mysql> insert into `test` (`code`, `name`) values ('munster', 'Munster');
mysql> insert into `test` (`code`, `name`) values ('münster', 'Münster');
ERROR 1062 (23000): Duplicate entry 'münster' for key 'test_code_unique'
mysql> SELECT * FROM test WHERE code='münster';
+----+---------+---------+
| id | name | code |
+----+---------+---------+
| 1 | Munster | munster |
+----+---------+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM test WHERE code='munster';
+----+---------+---------+
| id | name | code |
+----+---------+---------+
| 1 | Munster | munster |
+----+---------+---------+
1 row in set (0.00 sec)
If unique key is removed second insert works but a search returns 2 rows even if query is different:
mysql> drop table test;
CREATE TABLE `test` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`code` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mysql> insert into `test` (`code`, `name`) values ('munster', 'Munster');
mysql> insert into `test` (`code`, `name`) values ('münster', 'Münster');
mysql> SELECT * FROM test WHERE code='münster';
+----+----------+----------+
| id | name | code |
+----+----------+----------+
| 1 | Munster | munster |
| 2 | Münster | münster |
+----+----------+----------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM test WHERE code='munster';
+----+----------+----------+
| id | name | code |
+----+----------+----------+
| 1 | Munster | munster |
| 2 | Münster | münster |
+----+----------+----------+
2 rows in set (0.00 sec)
This has been tested on both MySQL 5.7 and MariaDB 10.2 and they both give same results.
What could be going wrong?

The reason for this seemingly mysterious problem is that you're using utf8mb4_unicode_ci collation, and that collation intentionally ignores differences in accented characters vs non-accented characters. See: https://dev.mysql.com/doc/refman/5.7/en/charset-general.html
To resolve this, change collation on code column to utf8mb4_bin, which will distinguish between accented characters and non-accented characters, and also between caSe.

Related

mysql won't let me change collation type

I'm trying to convert my table collation type from unicode to general, but mysql isn't allowing it. No errors are returned, but when I view the table, I can see that no changes are being made.
mysql> show variables like '%coll%';
+----------------------+-----------------+
| Variable_name | Value |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database | utf8_unicode_ci |
| collation_server | utf8_unicode_ci |
+----------------------+-----------------+
3 rows in set (0.00 sec)
mysql> show create table abc;
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| abc_test | CREATE TABLE `abc` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`somecolumn` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC |
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
mysql> alter table abc convert to charset utf8 collate utf8_general_ci;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table abc;
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| abc_test | CREATE TABLE `abc` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`somecolumn` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC |
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
Just use alter like this ALTER TABLE table_name COLLATE='utf8_general_ci';
Your table already has UTF8 charset. So, In my opinion you don't need to set it again. I can see that you are trying to convert it to charset utf8 again with your alter query.
I tried with mysql 5.7.18, and everything looks just fine. So which version are you using?
I set the collation variables same as yours, and created table and change the collation by the statements copied from above. And at last I got:
mysql> show create table abc;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| abc | CREATE TABLE `abc` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`somecolumn` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
mysql> select version();
+--------------+
| version() |
+--------------+
| 5.7.18-debug |
+--------------+
The "SHOW CREATE TABLE" doesn't print collation info because utf8_general_ci is the default collation of utf8.

strange "Duplicate entry" error of mysql

This might be an unicode related problem.
I have a mysql source file:
set names utf8;
STATUS;
drop table tianya;
create table tianya ( name char(50) not null primary key, passwd char (50) not null, email char(50));
insert into tianya values ("■■■■■■■■","68221",""),("12345678","098",""),("〡〢〣〤〥〦〧〨","1","");
And when I run this sql file, mysql reports:
ERROR 1062 (23000) at line 5: Duplicate entry '〡〢〣〤〥〦〧〨' for key 'PRIMARY'
as this image:
However, as we can see, the table is whole new, and the 3 primary keys are different from each other.
So, what causes this error?
=======add some extra info on 2015-Aug-28
mysql> create table tianya ( name char(50) not null primary key, passwd char (50) not null, email char(50));
Query OK, 0 rows affected (0.16 sec)
mysql>
mysql>
mysql> insert into tianya values ("■■■■■■■■","68221",""),("12345678","098","");
Query OK, 2 rows affected (0.19 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select name,hex(name) from tianya;
+--------------------------+--------------------------------------------------+
| name | hex(name) |
+--------------------------+--------------------------------------------------+
| ■■■■■■■■ | E296A0E296A0E296A0E296A0E296A0E296A0E296A0E296A0 |
| 12345678 | 3132333435363738 |
+--------------------------+--------------------------------------------------+
2 rows in set (0.03 sec)
mysql> insert into tianya values ("1234567","098",""),("〡〢〣〤〥〦〧〨","1","");
ERROR 1062 (23000): Duplicate entry '〡〢〣〤〥〦〧〨' for key 'PRIMARY'
mysql> delete from tianya;
Query OK, 2 rows affected (0.02 sec)
mysql> insert into tianya values ("1234567","098",""),("〡〢〣〤〥〦〧〨","1","");
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select name,hex(name) from tianya;
+--------------------------+--------------------------------------------------+
| name | hex(name) |
+--------------------------+--------------------------------------------------+
| 1234567 | 31323334353637 |
| 〡〢〣〤〥〦〧〨 | E380A1E380A2E380A3E380A4E380A5E380A6E380A7E380A8 |
+--------------------------+--------------------------------------------------+
2 rows in set (0.00 sec)
mysql> describe tianya;
+--------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+----------+------+-----+---------+-------+
| name | char(50) | NO | PRI | NULL | |
| passwd | char(50) | NO | | NULL | |
| email | char(50) | YES | | NULL | |
+--------+----------+------+-----+---------+-------+
3 rows in set (0.05 sec)
mysql> show create table tianya ( name char(50) not null primary key, passwd char (50) not null, email char(50));
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '( name char(50) not null primary key, passwd char (50) not null, email char(50))' at line 1
mysql> show create table tianya;
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tianya | CREATE TABLE `tianya` (
`name` char(50) COLLATE utf8_unicode_ci NOT NULL,
`passwd` char(50) COLLATE utf8_unicode_ci NOT NULL,
`email` char(50) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
====and another test:
xungeng#fl-ubuntu:~/tmp/a$ mysql -u root test < a.sql
Table Create Table
t2 CREATE TABLE `t2` (\n `name` varchar(10) COLLATE utf8_unicode_ci NOT NULL,\n UNIQUE KEY `name` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
ERROR 1062 (23000) at line 5: Duplicate entry 'hì' for key 'name'
xungeng#fl-ubuntu:~/tmp/a$ cat a.sql
set names utf8;
drop table if exists t2;
create table t2 (name varchar(10) not null unique key);
show create table t2;
insert into t2 values ("hí"),("hì");
Finally I know what had happened.
The collate utf_unicode_ci is case insensitive. This makes an insert of primary or unique key of 'TEST' failed if there is a key 'test' or 'Test'. I thought the collision is caused by '■■■■■■■■' and '〡〢〣〤〥〦〧〨', this misled you and also me. In fact the collision is caused by '12345678' and '〡〢〣〤〥〦〧〨'.
The symbols '〡〢〣〤〥〦〧〨' look so curious. They are ancient chinese symbols for number 12345678, respectively. [refer: https://zh.wikipedia.org/wiki/%E7%AD%B9%E7%AE%97 (chinese edition only)]
To resolve this problem, I changed utf8_unicode_ci to utf8_bin.
You can add to the charset of the table in the create statement. Append 'Set charset=utf8'. Maybe that helps. See http://dev.mysql.com/doc/refman/5.1/en/create-table.html
Section table-options

MySQL INSERT ... ON DUPLICATE KEY not updating table with no errors or warnings

So I have the following table:
mysql> show create table user_api_skills \G
*************************** 1. row ***************************
Table: user_api_skills
Create Table: CREATE TABLE `user_api_skills` (
`characterID` int(11) NOT NULL,
`typeID` int(11) NOT NULL,
`level` enum('0','1','2','3','4','5') NOT NULL DEFAULT '0',
`skillpoints` int(11) NOT NULL,
`currentTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`characterID`,`typeID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql>
And in that table a row which I am trying to insert/update:
mysql> SELECT * FROM `user_api_skills` WHERE `characterID` =93192782 AND `typeID` =3359;
+-------------+--------+-------+-------------+---------------------+
| characterID | typeID | level | skillpoints | currentTime |
+-------------+--------+-------+-------------+---------------------+
| 93192782 | 3359 | 3 | 135765 | 2013-09-30 16:58:35 |
+-------------+--------+-------+-------------+---------------------+
1 row in set (0.00 sec)
I believe my query is correctly formed and when executed it doesn't throw any errors or warnings:
mysql> INSERT INTO user_api_skills (characterID,typeID,level,skillpoints)
VALUES (93192782,3359,4,135765) ON DUPLICATE KEY UPDATE level=4,
skillpoints=135765,currentTime=NOW();
Query OK, 2 rows affected (0.22 sec)
I get 2 rows updated (as I would expect from an insert on dup update)
mysql> SELECT * FROM `user_api_skills` WHERE `characterID` =93192782 AND `typeID` =3359;
+-------------+--------+-------+-------------+---------------------+
| characterID | typeID | level | skillpoints | currentTime |
+-------------+--------+-------+-------------+---------------------+
| 93192782 | 3359 | 3 | 135765 | 2013-09-30 16:59:13 |
+-------------+--------+-------+-------------+---------------------+
1 row in set (0.00 sec)
mysql>
but the row itself only changes a single value (the currentTime). Can anybody explain why the other two fields are not updating?
Sorry, I have solved this myself. The level field is an ENUM and the query specified the new value as a number. Updating the query to the following resulted in the expected results.
mysql> INSERT INTO user_api_skills (characterID,typeID,level,skillpoints) VALUES
(93192782,3359,4,135765) ON DUPLICATE KEY UPDATE level='4', skillpoints=135765,
currentTime=NOW();
By providing a int to the update it updated to the one based number choice of the enum, so in this instance, the 4th choice is '3'.

Is it possible to disable automatic empty value insertion for fields without default value in MySQL?

Is there any configuration setting to stop MySQL from inserting an empty value for fields which don't have a default value and weren't specified in the insert statement? I'd much prefer it rejected an invalid insert statement.
Example - I have the following table in MySQL:
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`val1` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`val2` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
If you run this insert statement:
INSERT INTO test (val2) VALUES('a');
You get this response:
Query OK, 1 row affected, 1 warning (0.00 sec)
Response from show warnings:
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1364 | Field 'val1' doesn't have a default value |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
Response from SELECT * FROM test:
+----+------+------+
| id | val1 | val2 |
+----+------+------+
| 1 | | asdf |
+----+------+------+
1 row in set (0.00 sec)
You can enable 'strict' mode, which'd cause MySQL to fail any inserts that don't provide values for default-less fields.

Mysql primary key when using INSERT INTO ... ON DUPLICATE KEY UPDATE

My table structure is:
CREATE TABLE IF NOT EXISTS `users_settings_temp` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`userid` int(10) unsigned DEFAULT NULL,
`type` enum('type1','type2')
`data` text,
`date_created` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
What I am trying to do is:
Let say I want to insert a new entry, but I dont want it to be duplicate, after google around, i found this format:
INSERT INTO users_settings_temp(...)
ON DUPLICATE KEY UPDATE data = '{$data}'
I guess the problem is in my table, the primary key => id. How do I alter the table, so that I could use the:
INSERT INTO ... ON DUPLICATE KEY UPDATE
Can I use user_id + type as primary key? If yes, could you please show me how to do it?
CREATE TABLE IF NOT EXISTS `users_settings_temp` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`userid` int(10) unsigned DEFAULT NULL,
`type` enum('type1','type2'),
`data` text,
`date_created` int(11) DEFAULT NULL,
PRIMARY KEY (`id`, `type`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
When you do it like this then
a) specifying id works
mysql> INSERT INTO users_settings_temp VALUES (1, 2, 'type1', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO users_settings_temp VALUES (1, 2, 'type2', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
b) of course primary key is guaranteed to be unique
mysql> INSERT INTO users_settings_temp VALUES (1, 2, 'type2', 'keks', 5);
ERROR 1062 (23000): Duplicate entry '1-type2' for key 'PRIMARY'
c) letting database pull a new id works
mysql> INSERT INTO users_settings_temp VALUES (NULL, 2, 'type2', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO users_settings_temp VALUES (NULL, 2, 'type1', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
but will increase them always
mysql> SELECT * FROM users_settings_temp;
+----+--------+-------+------+--------------+
| id | userid | type | data | date_created |
+----+--------+-------+------+--------------+
| 1 | 2 | type1 | keks | 5 |
| 1 | 2 | type2 | keks | 5 |
| 2 | 2 | type2 | keks | 5 |
| 3 | 2 | type1 | keks | 5 |
+----+--------+-------+------+--------------+
4 rows in set (0.00 sec)
NOTES:
You should think if your id should still be autoincrement or not.
Also, can not think of a reason why date_created should be int(11) instead of datetime