MySQL Нелегальное сочетание сортировок

После просмотра моих журналов 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 '=' 

Alghout У меня есть UTF-8 по умолчанию в рамках доктрины cfg:

doctrine:
    dbal:
        charset:  UTF8

Кажется, что все мои таблицы MySQL находятся в latin1_swedish_ci, поэтому мой вопрос:

Можно ли вручную изменить сортировку на utf8_general_ci для всех моих таблиц без каких-либо осложнений/мер предосторожности?

Ответ 1

Полезно понять следующие определения:

  • A кодировка символов подробно описывает, как каждый символ представлен в двоичном формате (и, следовательно, хранится на компьютере). Например, символ é (U + 00E9, латинская маленькая буква E с острой) закодирован как 0xc3a9 в UTF-8 (который MySQL вызывает utf8) и 0xe9 в Windows-1252 (который MySQL вызывает latin1).

  • Набор символов - это алфавит символов, который может быть представлен с использованием заданной кодировки символов. Смутно, этот термин также используется для обозначения того же, что и кодировка символов.

  • сортировка - это упорядочение в наборе символов, поэтому строки можно сравнить. Например: MySQL latin1_swedish_ci collation рассматривает большинство акцентированных вариаций символа как эквивалент базового символа, тогда как его 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 равно строке, содержащей символ, который не может, существовать в этом колонка!

Если вы считаете, что столбец содержит такие символы, то вы, вероятно, писали в столбец, в то время как кодировка символов соединения была установлена ​​на что-то (например, latin1), что заставило 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 Синтаксис:

Чтобы изменить набор символов по умолчанию в таблице и все столбцы символов (CHAR, VARCHAR, TEXT) в новый набор символов, используйте следующее выражение:

ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;

Для столбца, который имеет тип данных VARCHAR или один из TEXT типы CONVERT TO CHARACTER SET при необходимости изменит тип данных, чтобы убедиться, что новый столбец достаточно длинный, чтобы хранить столько символов, сколько исходный столбец. Например, столбец TEXT содержит два байта длины, которые сохраняют длину байтов значений в столбце, максимум до 65535. Для столбца latin1 TEXT каждому символу требуется один байт, поэтому в столбце может храниться до 65 535 символов. Если столбец преобразуется в utf8, каждому символу может потребоваться до трех байтов, для максимально возможной длины 3 × 65 535 = 196 605 байт. Эта длина не будет соответствовать байтам длины TEXT, поэтому MySQL преобразует тип данных в MEDIUMTEXT, который является наименьшим строковым типом, для которого байты длины могут записывать значение 196,605. Аналогично, столбец VARCHAR может быть преобразован в MEDIUMTEXT.

Чтобы избежать изменений типа данных только что описанного типа, не используйте CONVERT TO CHARACTER SET. Вместо этого используйте MODIFY для изменения отдельных столбцов.

Ответ 2

Правильно. Я столкнулся с этой проблемой, и лучшее быстрое и быстрое решение -

         CONVERT(fos_user.username USING utf8)

Ответ 3

Просто преобразуйте таблицу символов, заданную командой, следующим образом:

ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8;