了解以下定义会有所帮助:
字符编码详细说明了每个符号如何以二进制表示(因此存储在计算机中)。例如,符号é(U+00E9,拉丁文小写字母E,带尖音符)是encoded,在UTF-8(MySQL 调用utf8)中为0xc3a9,在Windows-1252(其中MySQL 调用 latin1)。
字符集是可以使用给定字符编码表示的符号字母表。令人困惑的是,该术语也用于表示与字符编码相同的含义。
collation 是对字符集的排序,以便可以比较字符串。例如:MySQL 的 latin1_swedish_ci 排序规则将字符的大多数重音变体视为等同于基本字符,而其 latin1_general_ci 排序规则会将它们排序在下一个基本字符之前但不等同(还有其他更重要的差异:比如å、ä、ö和ß等字符的顺序。
MySQL 将决定应将哪种排序规则应用于给定表达式,如 Collation of Expressions 中所述:特别是,列的排序规则优先于字符串文字的排序规则。
查询的WHERE 子句比较以下字符串:
fos_user.username 中的一个值,以列的字符集 (Windows-1252) 编码,并表示对其排序规则 latin1_swedish_ci 的偏好(强制值为 2);与
字符串文字'Nrv⧧Kasi',以连接的字符集(UTF-8,由 Doctrine 配置)编码并表示对连接排序规则utf8_general_ci 的偏好(强制值为 4)。
由于这些字符串中的第一个具有比第二个更低的强制值,MySQL 尝试使用该字符串的排序规则执行比较:latin1_swedish_ci。为此,MySQL 尝试将第二个字符串转换为 latin1,但由于该字符集中不存在 ⧧ 字符,因此比较失败。
警告
应该暂停一下,考虑一下当前列的编码方式:您正在尝试过滤fos_user.username 等于包含不能字符的字符串的记录存在于该列中!
如果您认为列确实包含此类字符,那么您可能在连接字符编码设置为导致 MySQL 解释接收到字节序列为所有在 Windows-1252 字符集中的字符。
如果是这种情况,在继续之前,您应该修复您的数据!
-
将这些列转换为数据插入时使用的字符编码(如果与现有编码不同):
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
-
通过将这些列转换为binary 字符集来删除与这些列关联的编码信息:
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
-
通过将这些列转换为相关字符集,将实际传输数据的编码与这些列相关联。
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
请注意,如果从多字节编码转换,您可能需要增加列的大小(甚至更改其类型)以适应转换后字符串的最大可能长度。
一旦确定列被正确编码,就可以强制使用 Unicode 排序规则进行比较——
-
将值 fos_user.username 显式转换为 Unicode 字符集:
WHERE CONVERT(fos_user.username USING utf8) = ?
-
强制字符串文字具有比列更低的强制值(将导致列的值隐式转换为 UTF-8):
WHERE fos_user.username = ? COLLATE utf8_general_ci
或者,正如您所说,可以将列永久转换为 Unicode 编码并适当地设置其排序规则。
我可以将所有表格的排序规则手动更改为utf8_general_ci,而不会出现任何并发症/预防措施吗?
原则上考虑是Unicode编码比单字节字符集占用更多空间,所以:
另外,请注意,如 ALTER TABLE Syntax 中所述:
要将表默认字符集和所有字符列(CHAR、VARCHAR、TEXT)更改为新字符集,请使用如下语句:
ALTER TABLE tbl_name 转换为字符集 charset_name;
对于数据类型为VARCHAR 或TEXT 类型之一的列,CONVERT TO CHARACTER SET 将根据需要更改数据类型,以确保新列足够长以存储与原始列。例如,TEXT 列有两个长度字节,用于存储列中值的字节长度,最大值为 65,535。对于latin1TEXT 列,每个字符需要一个字节,因此该列最多可以存储65,535 个字符。如果将列转换为utf8,则每个字符最多可能需要三个字节,最大可能长度为 3 × 65,535 = 196,605 字节。该长度不适合 TEXT 列的长度字节,因此 MySQL 会将数据类型转换为 MEDIUMTEXT,这是长度字节可以记录值 196,605 的最小字符串类型。同样,VARCHAR 列可能会转换为 MEDIUMTEXT。
为避免刚刚描述的类型的数据类型更改,请勿使用CONVERT TO CHARACTER SET。相反,请使用 MODIFY 更改各个列。