关于symfony:MySQL非法混合排序规则

MySQL Illegal mix of collations

在查看我的Prod日志后,我有一些错误提到:

1
2
3
4
5
6
[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 '='

Alghout I在Cfg条令下有UTF-8默认值:

1
2
3
doctrine:
    dbal:
        charset:  UTF8

我所有的mysql表都在latin1_swedish_ci中,所以我的问题是:

对于我的所有表,我可以手动将排序规则更改为utf8_general_ci,而不需要任何复杂/预防措施吗?


有助于理解以下定义:好的。

  • 字符编码详细说明了如何用二进制(因此存储在计算机中)表示每个符号。例如,符号é(u+00e9,带锐音符的拉丁文小写字母e)在utf-8(mysql称为utf8)中编码为0xc3a9,在windows-1252(mysql称为latin1)中编码为0xe9。好的。

  • 字符集是可以用给定的字符编码表示的符号的字母表。令人困惑的是,这个术语也被用来表示与字符编码相同的含义。好的。

  • 排序规则是对字符集的排序,以便可以比较字符串。例如:mysql的latin1_swedish_ci排序将一个字符的大多数重音变化视为等同于基字符,而其latin1_general_ci排序将把它们排序在下一个基字符之前,但不等同(还有其他更重要的区别:如??等字符的顺序)。,??。好的。

MySQL将决定哪个排序规则应该应用于给定表达式,如表达式排序规则中所述:特别是,列的排序规则优先于字符串文字的排序规则。好的。

查询的WHERE子句比较以下字符串:好的。

  • fos_user.username中的一个值,编码在列的字符集(windows-1252)中,并表示对其排序规则的偏好latin1_swedish_ci(强制值为2);与好的。

  • 字符串文字'Nrv?Kasi',编码在连接的字符集(utf-8,按条令配置)中,表示优先选择连接的排序utf8_general_ci(强制值为4)。好的。

  • 由于第一个字符串的强制值低于第二个字符串,MySQL尝试使用该字符串的排序规则来执行比较:latin1_swedish_ci。为此,MySQL尝试将第二个字符串转换为latin1—但由于该字符集中不存在?字符,因此比较失败。好的。警告

    应该暂停片刻,考虑列当前是如何编码的:您正试图筛选fos_user.username等于包含该列中不存在的字符的字符串的记录!好的。

    如果您认为该列确实包含这样的字符,那么您可能会在连接字符编码设置为某个(例如latin1时写入该列,这会导致MySQL将接收到的字节序列解释为Windows-1252字符集中的所有字符。好的。

    如果是这种情况,在继续任何进一步操作之前,您应该修复您的数据!好的。

  • 如果与现有编码不同,请将这些列转换为数据插入时使用的字符编码:好的。

    1
    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
  • 通过将这些列转换为binary字符集,删除与这些列关联的编码信息:好的。

    1
    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
  • 将数据实际传输的编码与这些列关联起来,将它们转换为相关的字符集。好的。

    1
    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
  • 请注意,如果从多字节编码转换,可能需要增加列的大小(甚至更改其类型),以适应转换字符串的最大可能长度。好的。

    一旦确定列是正确编码的,就可以强制使用unicode排序规则通过—进行比较。好的。

    • 将值fos_user.username显式转换为Unicode字符集:好的。

      1
      WHERE CONVERT(fos_user.username USING utf8) = ?
    • 强制字符串文字具有比列更低的强制值(将导致列值隐式转换为utf-8):好的。

      1
      WHERE fos_user.username = ? COLLATE utf8_general_ci

    或者,如您所说,可以将列永久转换为Unicode编码并适当设置排序规则。好的。

    Can I manually change the collation to utf8_general_ci for all my tables without any complications/precautions ?

    Ok.

    主要考虑的是Unicode编码比单字节字符集占用更多空间,因此:好的。

    • 可能需要更多的存储空间;好的。

    • 比较可能较慢;以及好的。

    • 可能需要调整索引前缀长度(请注意,最大值以字节为单位,因此表示的字符可能比以前少)。好的。

    另外,请注意,如ALTER TABLE语法所述:好的。

    To change the table default character set and all character columns (CHAR, VARCHAR, TEXT) to a new character set, use a statement like this:

    Ok.

    1
    ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;

    For a column that has a data type of VARCHAR or one of the TEXT types, CONVERT TO CHARACTER SET will change the data type as necessary to ensure that the new column is long enough to store as many characters as the original column. For example, a TEXT column has two length bytes, which store the byte-length of values in the column, up to a maximum of 65,535. For a latin1 TEXT column, each character requires a single byte, so the column can store up to 65,535 characters. If the column is converted to utf8, each character might require up to three bytes, for a maximum possible length of 3 × 65,535 = 196,605 bytes. That length will not fit in a TEXT column's length bytes, so MySQL will convert the data type to MEDIUMTEXT, which is the smallest string type for which the length bytes can record a value of 196,605. Similarly, a VARCHAR column might be converted to MEDIUMTEXT.

    Ok.

    To avoid data type changes of the type just described, do not use CONVERT TO CHARACTER SET. Instead, use MODIFY to change individual columns.

    Ok.

    好啊。


    没错。我遇到这个问题,最好的快速解决方法是

    1
             CONVERT(fos_user.username USING utf8)


    只需按以下命令转换表的字符集:

    1
    ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8;