sf_*_*anb 18 mysql collation symfony fosuserbundle symfony-2.1
查看我的prod日志后,我提到了一些错误:
[2012-08-31 15:56:43] request.CRITICAL: Doctrine\DBAL\DBALException:
An exception occurred while executing 'SELECT t0.username ....... FROM fos_user t0 WHERE t0.username = ?'
with params {"1":"Nrv\u29e7Kasi"}:
SQLSTATE[HY000]: General error: 1267 Illegal mix of collations (latin1_swedish_ci,IMPLICIT)
and (utf8_general_ci,COERCIBLE) for operation '='
Run Code Online (Sandbox Code Playgroud)
Alghout我在学说cfg下有UTF-8默认值:
doctrine:
dbal:
charset: UTF8
Run Code Online (Sandbox Code Playgroud)
好像我的所有MySQL表都在latin1_swedish_ci,所以我的问题是:
我是否可以手动更改utf8_general_ci所有表格的校对而无需任何复杂/预防措施?
egg*_*yal 60
理解以下定义很有帮助:
甲字符编码详细每个符号如何以二进制表示(并因此存储在计算机).例如,符号é(U + 00E9,拉丁小字母E急性)被编码为0xc3a9在UTF-8 (其MySQL调用utf8)和0xe9在窗口1252(其MySQL调用latin1).
甲字符集是可使用给定字符编码表示符号的字母表.令人困惑的是,该术语也用于表示与字符编码相同的含义.
甲核对是在一个字符集的排序,以使字符串进行比较.例如:MySQL的latin1_swedish_ci排序规则将字符的大多数重音变体视为等同于基本字符,而其latin1_general_ci排序规则将在下一个基本字符之前对它们进行排序,但不是等效的(还有其他更重要的差异:例如字符的顺序喜欢å,ä,ö和ß).
MySQL将决定应该对给定表达式应用哪种排序规则,如表达式排序规则所述:特别是,列的排序优先于字符串文字的排序规则.
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-but,因为该?字符集中不存在该字符,因此比较失败.
我们应暂停一下,考虑当前列的编码方式:您正在尝试过滤记录,其中fos_user.username等于包含该列中不存在的字符的字符串!
如果你认为该列确实包含这样的字符,那么你可能写入列时连接字符编码被设置为某些东西(例如latin1)导致MySQL将接收到的字节序列解释为全部在Windows-1252字符中的字符组.
如果是这种情况,在继续之前你应该修复你的数据!
将这些列转换为数据插入时使用的字符编码,如果与现有编码不同:
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
Run Code Online (Sandbox Code Playgroud)删除与这些列关联的编码信息,方法是将它们转换为binary字符集:
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
Run Code Online (Sandbox Code Playgroud)通过将数据转换为相关字符集,将数据实际传输的编码与这些列相关联.
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
Run Code Online (Sandbox Code Playgroud)请注意,如果从多字节编码转换,您可能需要增加列的大小(甚至更改其类型),以便适应转换后的字符串的最大可能长度.
一旦确定列被正确编码,就可以强制通过以下任一方式使用Unicode整理进行比较 -
显式地将值fos_user.username转换为Unicode字符集:
WHERE CONVERT(fos_user.username USING utf8) = ?
Run Code Online (Sandbox Code Playgroud)强制字符串文字具有比列更低的强制性值(将导致将列的值隐式转换为UTF-8):
WHERE fos_user.username = ? COLLATE utf8_general_ci
Run Code Online (Sandbox Code Playgroud)或者,正如您所说,可以将列永久转换为Unicode编码并适当地设置其排序规则.
我是否可以手动更改
utf8_general_ci所有表格的校对而无需任何复杂/预防措施?
主要考虑因素是Unicode编码比单字节字符集占用更多空间,因此:
可能需要更多存储空间;
比较可能会更慢; 和
可能需要调整索引前缀长度(请注意,最大值以字节为单位,因此可能表示比以前更少的字符).
另外,请注意,如ALTER TABLE语法中所述:
要更改默认的表字符集和所有字符列(
CHAR,VARCHAR,TEXT)到一个新的字符集,使用这样的语句:ALTER TABLE tbl_name CONVERT TO CHARACTER SET 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用于更改单个列.
那就对了.我遇到了这个问题,最好的快速和快速的解决方案是
CONVERT(fos_user.username USING utf8)
Run Code Online (Sandbox Code Playgroud)