Ответ 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
для изменения отдельных столбцов.