我现在一直在研究这个问题几个小时,在SO,MySQL文档和其他地方,但仍然找不到满意的解决方案。问题是:
让SQL处理字符串的最简单方法是什么,就像SQLite一样,没有任何额外的“智能”转换?
例如,以下在SQLite中完美运行:
CREATE TABLE `dummy` (`key` VARCHAR(255) NOT NULL UNIQUE);
INSERT INTO `dummy` (`key`) VALUES ('one');
INSERT INTO `dummy` (`key`) VALUES ('one ');
INSERT INTO `dummy` (`key`) VALUES ('One');
INSERT INTO `dummy` (`key`) VALUES ('öne');
SELECT * FROM `dummy`;
但是,在MySQL中,使用以下设置:
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_bin
以及以下CREATE DATABASE
声明:
CREATE DATABASE `dummydb` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_bin;
它仍然在第二个INSERT
上失败。
我宁愿保持字符串列声明尽可能简单,SQLite的TEXT
是理想的。 看起来VARBINARY
是最佳选择,但我仍然希望听到您对任何其他更好 <的意见强>选项
附录:SHOW CREATE TABLE dummy
输出
mysql> SHOW CREATE TABLE dummy;
+-------+-----------------------------------------------------
| Table | Create Table
+-------+-----------------------------------------------------
| dummy | CREATE TABLE `dummy` (
`key` varchar(255) COLLATE utf8mb4_bin NOT NULL,
UNIQUE KEY `key` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+-----------------------------------------------------
1 row in set (0.00 sec)
答案 0 :(得分:1)
问题中显示的方法应该(大部分)在MySQL中正常工作,原因如下:
整理(不要与编码混淆)是定义如何对字符进行排序和比较的集合或规则,通常用于在数据库级别从文化角度复制用户期望(如果我搜索{{ 1}}我期待也可以找到cafe
。
整理对唯一约束起着重要作用,因为它建立了唯一的定义。
二进制排序规则专门用于忽略文化规则并在字节级别工作,因此café
是正确的选择。
MySQL允许使用列级粒度设置编码和排序规则的组合。
如果列定义缺少排序规则,则它将使用表级别一。
如果表定义缺少整理,则它将使用数据库级别一。
如果数据库定义缺少整理,则它将使用服务器级别一。
同样值得注意的是MySQL只要透明地在编码之间进行转换:
由于这最后一个原因,utf8mb4_bin
可能不是仍然是文本的列的最佳选择,因为它打开了从配置为使用ISO的连接中存储VARBINARY
的大门8859-1并且无法从配置为使用UTF-8的连接中正确检索它。
附注:显示的表定义可能会触发以下错误:
ERROR 1071(42000):指定密钥太长;最大密钥长度为767字节
索引的最大大小可能相对较小。来自docs:
如果启用innodb_large_prefix(默认值),则为索引键前缀 对于使用DYNAMIC或COMPRESSED的InnoDB表,限制为3072字节 行格式。如果禁用innodb_large_prefix,则为索引键前缀 对于任何行格式的表,限制为767字节。
innodb_large_prefix已弃用,将来会被删除 发布。 innodb_large_prefix是在MySQL 5.5中引入的,用于禁用 大索引键前缀,用于与早期版本的兼容 不支持大索引键前缀的InnoDB。
对于InnoDB表,索引键前缀长度限制为767字节 使用REDUNDANT或COMPACT行格式。例如,你可能会命中 此限制,列前缀索引超过255个字符 TEXT或VARCHAR列,假设一个utf8mb3字符集和 每个字符最多3个字节。
尝试使用超出限制的索引键前缀长度 返回错误。要避免复制配置中出现此类错误, 避免在master上启用innodb_large_prefix,如果它也不能 在奴隶上启用。
由于utf8_mb8为每个字符分配4个字节,因此767限制将溢出,只有192个字符。
我们还有一个问题:
café
赦免?
mysql> CREATE TABLE `dummy` (
-> `key` varchar(191) COLLATE utf8mb4_bin NOT NULL,
-> UNIQUE KEY `key` (`key`)
-> )
-> ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO `dummy` (`key`) VALUES ('one');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO `dummy` (`key`) VALUES ('one ');
ERROR 1062 (23000): Duplicate entry 'one ' for key 'key'
最后一个问题是MySQL排序规则的一个有趣的微妙之处。来自docs:
所有MySQL排序规则都是PADSPACE类型。这意味着所有CHAR, MySQL中的VARCHAR和TEXT值进行了比较而不考虑任何问题 尾随空格。在这种情况下,“比较”不包括 LIKE模式匹配运算符,尾随空格 显著
[...] 对于尾随填充字符被剥离的情况 比较忽略它们,如果列具有需要唯一的索引 值,插入仅在数量上不同的列值 尾随填充字符将导致重复键错误。
我敢说mysql> INSERT INTO `dummy` (`key`) VALUES ('One');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO `dummy` (`key`) VALUES ('öne');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM `dummy`;
+-----+
| key |
+-----+
| One |
| one |
| öne |
+-----+
3 rows in set (0.00 sec)
类型是解决这个问题的唯一方法......