关于Unicode和整理,如何使MySQL像SQLite一样处理字符串?

时间:2017-02-21 15:18:33

标签: mysql unicode collation

我现在一直在研究这个问题几个小时,在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)

1 个答案:

答案 0 :(得分:1)

问题中显示的方法应该(大部分)在MySQL中正常工作,原因如下:

  1. 整理(不要与编码混淆)是定义如何对字符进行排序和比较的集合或规则,通常用于在数据库级别从文化角度复制用户期望(如果我搜索{{ 1}}我期待也可以找到cafe

  2. 整理对唯一约束起着重要作用,因为它建立了唯一的定义。

  3. 二进制排序规则专门用于忽略文化规则并在字节级别工作,因此café是正确的选择。

  4. MySQL允许使用列级粒度设置编码和排序规则的组合。

  5. 如果列定义缺少排序规则,则它将使用表级别一。

  6. 如果表定义缺少整理,则它将使用数据库级别一。

  7. 如果数据库定义缺少整理,则它将使用服务器级别一。

  8. 同样值得注意的是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) 类型是解决这个问题的唯一方法......

相关问题